diff options
author | johnme@chromium.org <johnme@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-05-31 01:21:09 +0000 |
---|---|---|
committer | johnme@chromium.org <johnme@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-05-31 01:21:09 +0000 |
commit | 21b7765ae5142044dd95ce042ad93f9f2b963b17 (patch) | |
tree | e00da04bc07b7033b74b83bf7f76a1954ef1eddd /components | |
parent | 631fc091a7db836843ddd7e15a847068d72e43b4 (diff) | |
download | chromium_src-21b7765ae5142044dd95ce042ad93f9f2b963b17.zip chromium_src-21b7765ae5142044dd95ce042ad93f9f2b963b17.tar.gz chromium_src-21b7765ae5142044dd95ce042ad93f9f2b963b17.tar.bz2 |
This patch splits GCMDriver into an abstract interface and an
implementation thereof for desktop platforms. It also provides a stub
Android implementation, to be fleshed out in subsequent patches.
BUG=350384
TEST=existing tests
TBR=pavely@chromium.org for updates to invalidation tests
Review URL: https://codereview.chromium.org/278493002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@273977 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'components')
-rw-r--r-- | components/components_tests.gyp | 3 | ||||
-rw-r--r-- | components/gcm_driver.gypi | 8 | ||||
-rw-r--r-- | components/gcm_driver/fake_gcm_driver.cc | 70 | ||||
-rw-r--r-- | components/gcm_driver/fake_gcm_driver.h | 52 | ||||
-rw-r--r-- | components/gcm_driver/gcm_driver.cc | 781 | ||||
-rw-r--r-- | components/gcm_driver/gcm_driver.h | 158 | ||||
-rw-r--r-- | components/gcm_driver/gcm_driver_android.cc | 63 | ||||
-rw-r--r-- | components/gcm_driver/gcm_driver_android.h | 38 | ||||
-rw-r--r-- | components/gcm_driver/gcm_driver_desktop.cc | 809 | ||||
-rw-r--r-- | components/gcm_driver/gcm_driver_desktop.h | 171 | ||||
-rw-r--r-- | components/gcm_driver/gcm_driver_desktop_unittest.cc (renamed from components/gcm_driver/gcm_driver_unittest.cc) | 5 |
11 files changed, 1236 insertions, 922 deletions
diff --git a/components/components_tests.gyp b/components/components_tests.gyp index f5f530d..99d3307 100644 --- a/components/components_tests.gyp +++ b/components/components_tests.gyp @@ -97,7 +97,7 @@ 'domain_reliability/util_unittest.cc', 'enhanced_bookmarks/image_store_unittest.cc', 'feedback/feedback_uploader_unittest.cc', - 'gcm_driver/gcm_driver_unittest.cc', + 'gcm_driver/gcm_driver_desktop_unittest.cc', 'invalidation/invalidation_logger_unittest.cc', 'json_schema/json_schema_validator_unittest.cc', 'json_schema/json_schema_validator_unittest_base.cc', @@ -432,6 +432,7 @@ }], ['OS == "android"', { 'sources!': [ + 'gcm_driver/gcm_driver_desktop_unittest.cc', 'feedback/feedback_uploader_unittest.cc', 'signin/core/browser/mutable_profile_oauth2_token_service_unittest.cc', 'storage_monitor/media_storage_util_unittest.cc', diff --git a/components/gcm_driver.gypi b/components/gcm_driver.gypi index a2e5455..7b3bb4a 100644 --- a/components/gcm_driver.gypi +++ b/components/gcm_driver.gypi @@ -28,6 +28,8 @@ 'gcm_driver/gcm_driver.h', 'gcm_driver/gcm_driver_android.cc', 'gcm_driver/gcm_driver_android.h', + 'gcm_driver/gcm_driver_desktop.cc', + 'gcm_driver/gcm_driver_desktop.h', 'gcm_driver/system_encryptor.cc', 'gcm_driver/system_encryptor.h', ], @@ -36,6 +38,10 @@ 'dependencies': [ 'gcm_driver_jni_headers', ], + 'sources!': [ + 'gcm_driver/gcm_driver_desktop.cc', + 'gcm_driver/gcm_driver_desktop.h', + ], }], ], }, @@ -57,6 +63,8 @@ 'gcm_driver/fake_gcm_client.h', 'gcm_driver/fake_gcm_client_factory.cc', 'gcm_driver/fake_gcm_client_factory.h', + 'gcm_driver/fake_gcm_driver.cc', + 'gcm_driver/fake_gcm_driver.h', ], }, ], diff --git a/components/gcm_driver/fake_gcm_driver.cc b/components/gcm_driver/fake_gcm_driver.cc new file mode 100644 index 0000000..667b824 --- /dev/null +++ b/components/gcm_driver/fake_gcm_driver.cc @@ -0,0 +1,70 @@ +// 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/fake_gcm_driver.h" + +namespace gcm { + +FakeGCMDriver::FakeGCMDriver() { +} + +FakeGCMDriver::~FakeGCMDriver() { +} + +void FakeGCMDriver::Shutdown() { +} + +void FakeGCMDriver::AddAppHandler( + const std::string& app_id, GCMAppHandler* handler) { +} + +void FakeGCMDriver::RemoveAppHandler(const std::string& app_id) { +} + +void FakeGCMDriver::Enable() { +} + +void FakeGCMDriver::Disable() { +} + +void FakeGCMDriver::Register(const std::string& app_id, + const std::vector<std::string>& sender_ids, + const RegisterCallback& callback) { +} + +void FakeGCMDriver::Unregister(const std::string& app_id, + const UnregisterCallback& callback) { +} + +void FakeGCMDriver::Send(const std::string& app_id, + const std::string& receiver_id, + const GCMClient::OutgoingMessage& message, + const SendCallback& callback) { +} + +GCMClient* FakeGCMDriver::GetGCMClientForTesting() const { + return NULL; +} + +bool FakeGCMDriver::IsStarted() const { + return true; +} + +bool FakeGCMDriver::IsGCMClientReady() const { + return true; +} + +void FakeGCMDriver::GetGCMStatistics(const GetGCMStatisticsCallback& callback, + bool clear_logs) { +} + +void FakeGCMDriver::SetGCMRecording(const GetGCMStatisticsCallback& callback, + bool recording) { +} + +std::string FakeGCMDriver::SignedInUserName() const { + return std::string(); +} + +} // namespace gcm diff --git a/components/gcm_driver/fake_gcm_driver.h b/components/gcm_driver/fake_gcm_driver.h new file mode 100644 index 0000000..b08f9c3 --- /dev/null +++ b/components/gcm_driver/fake_gcm_driver.h @@ -0,0 +1,52 @@ +// 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 COMPONENTS_GCM_DRIVER_FAKE_GCM_DRIVER_H_ +#define COMPONENTS_GCM_DRIVER_FAKE_GCM_DRIVER_H_ + +#include "base/compiler_specific.h" +#include "base/macros.h" +#include "components/gcm_driver/gcm_driver.h" + +namespace gcm { + +class FakeGCMDriver : public GCMDriver { + public: + FakeGCMDriver(); + virtual ~FakeGCMDriver(); + + // GCMDriver overrides: + virtual void Shutdown() OVERRIDE; + virtual void AddAppHandler(const std::string& app_id, + GCMAppHandler* handler) OVERRIDE; + virtual void RemoveAppHandler(const std::string& app_id) OVERRIDE; + + // GCMDriver implementation: + virtual void Enable() OVERRIDE; + virtual void Disable() OVERRIDE; + virtual void Register(const std::string& app_id, + const std::vector<std::string>& sender_ids, + const RegisterCallback& callback) OVERRIDE; + virtual void Unregister(const std::string& app_id, + const UnregisterCallback& callback) OVERRIDE; + virtual void Send(const std::string& app_id, + const std::string& receiver_id, + const GCMClient::OutgoingMessage& message, + const SendCallback& callback) OVERRIDE; + virtual GCMClient* GetGCMClientForTesting() const OVERRIDE; + virtual bool IsStarted() const OVERRIDE; + virtual bool IsGCMClientReady() const OVERRIDE; + virtual void GetGCMStatistics(const GetGCMStatisticsCallback& callback, + bool clear_logs) OVERRIDE; + virtual void SetGCMRecording(const GetGCMStatisticsCallback& callback, + bool recording) OVERRIDE; + virtual std::string SignedInUserName() const OVERRIDE; + + private: + DISALLOW_COPY_AND_ASSIGN(FakeGCMDriver); +}; + +} // namespace gcm + +#endif // COMPONENTS_GCM_DRIVER_FAKE_GCM_DRIVER_H_ diff --git a/components/gcm_driver/gcm_driver.cc b/components/gcm_driver/gcm_driver.cc index e41972e..866e6ca 100644 --- a/components/gcm_driver/gcm_driver.cc +++ b/components/gcm_driver/gcm_driver.cc @@ -4,818 +4,41 @@ #include "components/gcm_driver/gcm_driver.h" -#include <algorithm> -#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/sequenced_task_runner.h" -#include "base/threading/sequenced_worker_pool.h" #include "components/gcm_driver/gcm_app_handler.h" -#include "components/gcm_driver/gcm_client_factory.h" -#include "components/gcm_driver/system_encryptor.h" -#include "google_apis/gaia/oauth2_token_service.h" -#include "net/url_request/url_request_context_getter.h" namespace gcm { -// Helper class to save tasks to run until we're ready to execute them. -class GCMDriver::DelayedTaskController { - public: - DelayedTaskController(); - ~DelayedTaskController(); - - // Adds a task that will be invoked once we're ready. - void AddTask(const base::Closure& task); - - // Sets ready status. It is ready only when check-in is completed and - // the GCMClient is fully initialized. - void SetReady(); - - // Returns true if it is ready to perform tasks. - bool CanRunTaskWithoutDelay() const; - - private: - void RunTasks(); - - // Flag that indicates that GCM is ready. - bool ready_; - - std::vector<base::Closure> delayed_tasks_; - - DISALLOW_COPY_AND_ASSIGN(DelayedTaskController); -}; - -GCMDriver::DelayedTaskController::DelayedTaskController() : ready_(false) { -} - -GCMDriver::DelayedTaskController::~DelayedTaskController() { -} - -void GCMDriver::DelayedTaskController::AddTask(const base::Closure& task) { - delayed_tasks_.push_back(task); -} - -void GCMDriver::DelayedTaskController::SetReady() { - ready_ = true; - RunTasks(); -} - -bool GCMDriver::DelayedTaskController::CanRunTaskWithoutDelay() const { - return ready_; -} - -void GCMDriver::DelayedTaskController::RunTasks() { - DCHECK(ready_); - - for (size_t i = 0; i < delayed_tasks_.size(); ++i) - delayed_tasks_[i].Run(); - delayed_tasks_.clear(); -} - -class GCMDriver::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. - virtual void OnRegisterFinished(const std::string& app_id, - const std::string& registration_id, - GCMClient::Result result) OVERRIDE; - virtual void OnUnregisterFinished(const std::string& app_id, - GCMClient::Result result) OVERRIDE; - virtual void OnSendFinished(const std::string& app_id, - const std::string& message_id, - GCMClient::Result result) OVERRIDE; - virtual void OnMessageReceived( - const std::string& app_id, - const GCMClient::IncomingMessage& message) OVERRIDE; - virtual void OnMessagesDeleted(const std::string& app_id) OVERRIDE; - virtual void OnMessageSendError( - const std::string& app_id, - const GCMClient::SendErrorDetails& send_error_details) OVERRIDE; - virtual void OnGCMReady() OVERRIDE; - virtual void OnActivityRecorded() OVERRIDE; - - // Called on IO thread. - void Initialize( - scoped_ptr<GCMClientFactory> gcm_client_factory, - const GCMClient::ChromeBuildInfo& chrome_build_info, - const base::FilePath& store_path, - const std::vector<std::string>& account_ids, - const scoped_refptr<net::URLRequestContextGetter>& request_context, - const scoped_refptr<base::SequencedTaskRunner> blocking_task_runner); - void Start(const base::WeakPtr<GCMDriver>& service); - void Stop(); - void CheckOut(); - 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 GCMClient::OutgoingMessage& message); - void GetGCMStatistics(bool clear_logs); - void SetGCMRecording(bool recording); - - // 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<GCMDriver> service_; - - scoped_ptr<GCMClient> gcm_client_; - - DISALLOW_COPY_AND_ASSIGN(IOWorker); -}; - -GCMDriver::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()); -} - -GCMDriver::IOWorker::~IOWorker() { - DCHECK(io_thread_->RunsTasksOnCurrentThread()); -} - -void GCMDriver::IOWorker::Initialize( - scoped_ptr<GCMClientFactory> gcm_client_factory, - const GCMClient::ChromeBuildInfo& chrome_build_info, - const base::FilePath& store_path, - const std::vector<std::string>& account_ids, - const scoped_refptr<net::URLRequestContextGetter>& request_context, - const scoped_refptr<base::SequencedTaskRunner> blocking_task_runner) { - DCHECK(io_thread_->RunsTasksOnCurrentThread()); - - gcm_client_ = gcm_client_factory->BuildInstance(); - - gcm_client_->Initialize(chrome_build_info, - store_path, - account_ids, - blocking_task_runner, - request_context, - make_scoped_ptr<Encryptor>(new SystemEncryptor), - this); -} - -void GCMDriver::IOWorker::OnRegisterFinished( - const std::string& app_id, - const std::string& registration_id, - GCMClient::Result result) { - DCHECK(io_thread_->RunsTasksOnCurrentThread()); - - ui_thread_->PostTask( - FROM_HERE, - base::Bind(&GCMDriver::RegisterFinished, service_, app_id, - registration_id, result)); -} - -void GCMDriver::IOWorker::OnUnregisterFinished(const std::string& app_id, - GCMClient::Result result) { - DCHECK(io_thread_->RunsTasksOnCurrentThread()); - - ui_thread_->PostTask( - FROM_HERE, - base::Bind(&GCMDriver::UnregisterFinished, service_, app_id, result)); -} - -void GCMDriver::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(&GCMDriver::SendFinished, service_, app_id, message_id, - result)); -} - -void GCMDriver::IOWorker::OnMessageReceived( - const std::string& app_id, - const GCMClient::IncomingMessage& message) { - DCHECK(io_thread_->RunsTasksOnCurrentThread()); - - ui_thread_->PostTask( - FROM_HERE, - base::Bind(&GCMDriver::MessageReceived, service_, app_id, message)); -} - -void GCMDriver::IOWorker::OnMessagesDeleted(const std::string& app_id) { - DCHECK(io_thread_->RunsTasksOnCurrentThread()); - - ui_thread_->PostTask( - FROM_HERE, - base::Bind(&GCMDriver::MessagesDeleted, service_, app_id)); -} - -void GCMDriver::IOWorker::OnMessageSendError( - const std::string& app_id, - const GCMClient::SendErrorDetails& send_error_details) { - DCHECK(io_thread_->RunsTasksOnCurrentThread()); - - ui_thread_->PostTask( - FROM_HERE, - base::Bind(&GCMDriver::MessageSendError, service_, app_id, - send_error_details)); -} - -void GCMDriver::IOWorker::OnGCMReady() { - ui_thread_->PostTask( - FROM_HERE, - base::Bind(&GCMDriver::GCMClientReady, service_)); -} - -void GCMDriver::IOWorker::OnActivityRecorded() { - DCHECK(io_thread_->RunsTasksOnCurrentThread()); - // When an activity is recorded, get all the stats and refresh the UI of - // gcm-internals page. - GetGCMStatistics(false); -} - -void GCMDriver::IOWorker::Start(const base::WeakPtr<GCMDriver>& service) { - DCHECK(io_thread_->RunsTasksOnCurrentThread()); - - service_ = service; - gcm_client_->Start(); -} - -void GCMDriver::IOWorker::Stop() { - DCHECK(io_thread_->RunsTasksOnCurrentThread()); - - gcm_client_->Stop(); -} - -void GCMDriver::IOWorker::CheckOut() { - DCHECK(io_thread_->RunsTasksOnCurrentThread()); - - gcm_client_->CheckOut(); - - // Note that we still need to keep GCMClient instance alive since the - // GCMDriver may check in again. -} - -void GCMDriver::IOWorker::Register( - const std::string& app_id, - const std::vector<std::string>& sender_ids) { - DCHECK(io_thread_->RunsTasksOnCurrentThread()); - - gcm_client_->Register(app_id, sender_ids); -} - -void GCMDriver::IOWorker::Unregister(const std::string& app_id) { - DCHECK(io_thread_->RunsTasksOnCurrentThread()); - - gcm_client_->Unregister(app_id); -} - -void GCMDriver::IOWorker::Send(const std::string& app_id, - const std::string& receiver_id, - const GCMClient::OutgoingMessage& message) { - DCHECK(io_thread_->RunsTasksOnCurrentThread()); - - gcm_client_->Send(app_id, receiver_id, message); -} - -void GCMDriver::IOWorker::GetGCMStatistics(bool clear_logs) { - DCHECK(io_thread_->RunsTasksOnCurrentThread()); - gcm::GCMClient::GCMStatistics stats; - - if (gcm_client_.get()) { - if (clear_logs) - gcm_client_->ClearActivityLogs(); - stats = gcm_client_->GetStatistics(); - } - - ui_thread_->PostTask( - FROM_HERE, - base::Bind(&GCMDriver::GetGCMStatisticsFinished, service_, stats)); -} - -void GCMDriver::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(&GCMDriver::GetGCMStatisticsFinished, service_, stats)); -} - -GCMDriver::GCMDriver( - scoped_ptr<GCMClientFactory> gcm_client_factory, - scoped_ptr<IdentityProvider> identity_provider, - const GCMClient::ChromeBuildInfo& chrome_build_info, - 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) - : gcm_enabled_(true), - gcm_client_ready_(false), - identity_provider_(identity_provider.Pass()), - ui_thread_(ui_thread), - io_thread_(io_thread), - weak_ptr_factory_(this) { - // Get the list of available accounts. - std::vector<std::string> account_ids; -#if !defined(OS_ANDROID) - account_ids = identity_provider_->GetTokenService()->GetAccounts(); -#endif - - // 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(&GCMDriver::IOWorker::Initialize, - base::Unretained(io_worker_.get()), - base::Passed(&gcm_client_factory), - chrome_build_info, - store_path, - account_ids, - request_context, - blocking_task_runner)); - - identity_provider_->AddObserver(this); -} - -GCMDriver::GCMDriver() - : gcm_enabled_(true), - gcm_client_ready_(false), - weak_ptr_factory_(this) { +GCMDriver::GCMDriver() { } GCMDriver::~GCMDriver() { } -void GCMDriver::Enable() { - DCHECK(ui_thread_->RunsTasksOnCurrentThread()); - - if (gcm_enabled_) - return; - gcm_enabled_ = true; - - EnsureStarted(); -} - -void GCMDriver::Disable() { - DCHECK(ui_thread_->RunsTasksOnCurrentThread()); - - if (!gcm_enabled_) - return; - gcm_enabled_ = false; - - Stop(); -} - -void GCMDriver::Stop() { - DCHECK(ui_thread_->RunsTasksOnCurrentThread()); - - // No need to stop GCM service if not started yet. - if (account_id_.empty()) - return; - - RemoveCachedData(); - - io_thread_->PostTask( - FROM_HERE, - base::Bind(&GCMDriver::IOWorker::Stop, - base::Unretained(io_worker_.get()))); -} - void GCMDriver::Shutdown() { - DCHECK(ui_thread_->RunsTasksOnCurrentThread()); - identity_provider_->RemoveObserver(this); for (GCMAppHandlerMap::const_iterator iter = app_handlers_.begin(); iter != app_handlers_.end(); ++iter) { iter->second->ShutdownHandler(); } app_handlers_.clear(); - io_thread_->DeleteSoon(FROM_HERE, io_worker_.release()); } void GCMDriver::AddAppHandler(const std::string& app_id, GCMAppHandler* handler) { - DCHECK(ui_thread_->RunsTasksOnCurrentThread()); DCHECK(!app_id.empty()); DCHECK(handler); DCHECK(app_handlers_.find(app_id) == app_handlers_.end()); - app_handlers_[app_id] = handler; - - // Ensures that the GCM service is started when there is an interest. - EnsureStarted(); } void GCMDriver::RemoveAppHandler(const std::string& app_id) { - DCHECK(ui_thread_->RunsTasksOnCurrentThread()); DCHECK(!app_id.empty()); - app_handlers_.erase(app_id); - - // Stops the GCM service when no app intends to consume it. - if (app_handlers_.empty()) - Stop(); -} - -void GCMDriver::Register(const std::string& app_id, - const std::vector<std::string>& sender_ids, - const RegisterCallback& callback) { - DCHECK(ui_thread_->RunsTasksOnCurrentThread()); - DCHECK(!app_id.empty()); - DCHECK(!sender_ids.empty()); - DCHECK(!callback.is_null()); - - GCMClient::Result result = EnsureStarted(); - if (result != GCMClient::SUCCESS) { - callback.Run(std::string(), result); - return; - } - - // If previous un/register operation is still in progress, bail out. - if (IsAsyncOperationPending(app_id)) { - callback.Run(std::string(), GCMClient::ASYNC_OPERATION_PENDING); - return; - } - - register_callbacks_[app_id] = callback; - - // Delay the register operation until GCMClient is ready. - if (!delayed_task_controller_->CanRunTaskWithoutDelay()) { - delayed_task_controller_->AddTask(base::Bind(&GCMDriver::DoRegister, - weak_ptr_factory_.GetWeakPtr(), - app_id, - sender_ids)); - return; - } - - DoRegister(app_id, sender_ids); -} - -void GCMDriver::DoRegister(const std::string& app_id, - const std::vector<std::string>& sender_ids) { - DCHECK(ui_thread_->RunsTasksOnCurrentThread()); - std::map<std::string, RegisterCallback>::iterator callback_iter = - register_callbacks_.find(app_id); - if (callback_iter == register_callbacks_.end()) { - // The callback could have been removed when the app is uninstalled. - return; - } - - // Normalize the sender IDs by making them sorted. - std::vector<std::string> normalized_sender_ids = sender_ids; - std::sort(normalized_sender_ids.begin(), normalized_sender_ids.end()); - - io_thread_->PostTask( - FROM_HERE, - base::Bind(&GCMDriver::IOWorker::Register, - base::Unretained(io_worker_.get()), - app_id, - normalized_sender_ids)); -} - -void GCMDriver::Unregister(const std::string& app_id, - const UnregisterCallback& callback) { - DCHECK(ui_thread_->RunsTasksOnCurrentThread()); - DCHECK(!app_id.empty()); - DCHECK(!callback.is_null()); - - GCMClient::Result result = EnsureStarted(); - if (result != GCMClient::SUCCESS) { - callback.Run(result); - return; - } - - // If previous un/register operation is still in progress, bail out. - if (IsAsyncOperationPending(app_id)) { - callback.Run(GCMClient::ASYNC_OPERATION_PENDING); - return; - } - - unregister_callbacks_[app_id] = callback; - - // Delay the unregister operation until GCMClient is ready. - if (!delayed_task_controller_->CanRunTaskWithoutDelay()) { - delayed_task_controller_->AddTask(base::Bind(&GCMDriver::DoUnregister, - weak_ptr_factory_.GetWeakPtr(), - app_id)); - return; - } - - DoUnregister(app_id); -} - -void GCMDriver::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(&GCMDriver::IOWorker::Unregister, - base::Unretained(io_worker_.get()), - app_id)); -} - -void GCMDriver::Send(const std::string& app_id, - const std::string& receiver_id, - const GCMClient::OutgoingMessage& message, - const SendCallback& callback) { - DCHECK(ui_thread_->RunsTasksOnCurrentThread()); - DCHECK(!app_id.empty()); - DCHECK(!receiver_id.empty()); - DCHECK(!callback.is_null()); - - GCMClient::Result result = EnsureStarted(); - if (result != GCMClient::SUCCESS) { - callback.Run(std::string(), result); - return; - } - - // If the message with send ID is still in progress, bail out. - std::pair<std::string, std::string> key(app_id, message.id); - if (send_callbacks_.find(key) != send_callbacks_.end()) { - callback.Run(message.id, GCMClient::INVALID_PARAMETER); - return; - } - - send_callbacks_[key] = callback; - - // Delay the send operation until all GCMClient is ready. - if (!delayed_task_controller_->CanRunTaskWithoutDelay()) { - delayed_task_controller_->AddTask(base::Bind(&GCMDriver::DoSend, - weak_ptr_factory_.GetWeakPtr(), - app_id, - receiver_id, - message)); - return; - } - - DoSend(app_id, receiver_id, message); -} - -void GCMDriver::DoSend(const std::string& app_id, - const std::string& receiver_id, - const GCMClient::OutgoingMessage& message) { - DCHECK(ui_thread_->RunsTasksOnCurrentThread()); - io_thread_->PostTask( - FROM_HERE, - base::Bind(&GCMDriver::IOWorker::Send, - base::Unretained(io_worker_.get()), - app_id, - receiver_id, - message)); -} - -GCMClient* GCMDriver::GetGCMClientForTesting() const { - DCHECK(ui_thread_->RunsTasksOnCurrentThread()); - return io_worker_ ? io_worker_->gcm_client_for_testing() : NULL; -} - -bool GCMDriver::IsStarted() const { - DCHECK(ui_thread_->RunsTasksOnCurrentThread()); - return !account_id_.empty(); -} - -bool GCMDriver::IsGCMClientReady() const { - DCHECK(ui_thread_->RunsTasksOnCurrentThread()); - return gcm_client_ready_; -} - -void GCMDriver::GetGCMStatistics(const GetGCMStatisticsCallback& callback, - bool clear_logs) { - DCHECK(ui_thread_->RunsTasksOnCurrentThread()); - DCHECK(!callback.is_null()); - - request_gcm_statistics_callback_ = callback; - io_thread_->PostTask( - FROM_HERE, - base::Bind(&GCMDriver::IOWorker::GetGCMStatistics, - base::Unretained(io_worker_.get()), - clear_logs)); -} - -void GCMDriver::SetGCMRecording(const GetGCMStatisticsCallback& callback, - bool recording) { - DCHECK(ui_thread_->RunsTasksOnCurrentThread()); - - request_gcm_statistics_callback_ = callback; - io_thread_->PostTask( - FROM_HERE, - base::Bind(&GCMDriver::IOWorker::SetGCMRecording, - base::Unretained(io_worker_.get()), - recording)); -} - -void GCMDriver::OnActiveAccountLogin() { - EnsureStarted(); -} - -void GCMDriver::OnActiveAccountLogout() { - CheckOut(); -} - -GCMClient::Result GCMDriver::EnsureStarted() { - DCHECK(ui_thread_->RunsTasksOnCurrentThread()); - - if (!gcm_enabled_) - return GCMClient::GCM_DISABLED; - - // Have any app requested the service? - if (app_handlers_.empty()) - return GCMClient::UNKNOWN_ERROR; - - // Is the user signed in? - const std::string account_id = identity_provider_->GetActiveAccountId(); - if (account_id.empty()) - return GCMClient::NOT_SIGNED_IN; - - // CheckIn could be called more than once when: - // 1) The password changes. - // 2) Register/send function calls it to ensure CheckIn is done. - if (account_id_ == account_id) - return GCMClient::SUCCESS; - account_id_ = account_id; - - DCHECK(!delayed_task_controller_); - delayed_task_controller_.reset(new DelayedTaskController); - - // Note that we need to pass weak pointer again since the existing weak - // pointer in IOWorker might have been invalidated when check-out occurs. - io_thread_->PostTask( - FROM_HERE, - base::Bind(&GCMDriver::IOWorker::Start, - base::Unretained(io_worker_.get()), - weak_ptr_factory_.GetWeakPtr())); - - return GCMClient::SUCCESS; -} - -void GCMDriver::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(); - - account_id_.clear(); - gcm_client_ready_ = false; - delayed_task_controller_.reset(); - register_callbacks_.clear(); - send_callbacks_.clear(); -} - -void GCMDriver::CheckOut() { - DCHECK(ui_thread_->RunsTasksOnCurrentThread()); - - // We still proceed with the check-out logic even if the check-in is not - // initiated in the current session. This will make sure that all the - // persisted data written previously will get purged. - - RemoveCachedData(); - - io_thread_->PostTask( - FROM_HERE, - base::Bind(&GCMDriver::IOWorker::CheckOut, - base::Unretained(io_worker_.get()))); -} - -bool GCMDriver::IsAsyncOperationPending(const std::string& app_id) const { - DCHECK(ui_thread_->RunsTasksOnCurrentThread()); - return register_callbacks_.find(app_id) != register_callbacks_.end() || - unregister_callbacks_.find(app_id) != unregister_callbacks_.end(); -} - -void GCMDriver::RegisterFinished(const std::string& app_id, - const std::string& registration_id, - GCMClient::Result result) { - DCHECK(ui_thread_->RunsTasksOnCurrentThread()); - - std::map<std::string, RegisterCallback>::iterator callback_iter = - register_callbacks_.find(app_id); - if (callback_iter == register_callbacks_.end()) { - // The callback could have been removed when the app is uninstalled. - return; - } - - RegisterCallback callback = callback_iter->second; - register_callbacks_.erase(callback_iter); - callback.Run(registration_id, result); -} - -void GCMDriver::UnregisterFinished(const std::string& app_id, - GCMClient::Result result) { - DCHECK(ui_thread_->RunsTasksOnCurrentThread()); - - std::map<std::string, UnregisterCallback>::iterator callback_iter = - unregister_callbacks_.find(app_id); - if (callback_iter == unregister_callbacks_.end()) - return; - - UnregisterCallback callback = callback_iter->second; - unregister_callbacks_.erase(callback_iter); - callback.Run(result); -} - -void GCMDriver::SendFinished(const std::string& app_id, - const std::string& message_id, - GCMClient::Result result) { - DCHECK(ui_thread_->RunsTasksOnCurrentThread()); - - std::map<std::pair<std::string, std::string>, SendCallback>::iterator - callback_iter = send_callbacks_.find( - std::pair<std::string, std::string>(app_id, message_id)); - if (callback_iter == send_callbacks_.end()) { - // The callback could have been removed when the app is uninstalled. - return; - } - - SendCallback callback = callback_iter->second; - send_callbacks_.erase(callback_iter); - callback.Run(message_id, result); -} - -void GCMDriver::MessageReceived(const std::string& app_id, - GCMClient::IncomingMessage message) { - DCHECK(ui_thread_->RunsTasksOnCurrentThread()); - - // Drop the event if signed out. - if (account_id_.empty()) - return; - - GetAppHandler(app_id)->OnMessage(app_id, message); -} - -void GCMDriver::MessagesDeleted(const std::string& app_id) { - DCHECK(ui_thread_->RunsTasksOnCurrentThread()); - - // Drop the event if signed out. - if (account_id_.empty()) - return; - - GetAppHandler(app_id)->OnMessagesDeleted(app_id); -} - -void GCMDriver::MessageSendError( - const std::string& app_id, - const GCMClient::SendErrorDetails& send_error_details) { - DCHECK(ui_thread_->RunsTasksOnCurrentThread()); - - // Drop the event if signed out. - if (account_id_.empty()) - return; - - GetAppHandler(app_id)->OnSendError(app_id, send_error_details); -} - -void GCMDriver::GCMClientReady() { - DCHECK(ui_thread_->RunsTasksOnCurrentThread()); - - if (gcm_client_ready_) - return; - gcm_client_ready_ = true; - - delayed_task_controller_->SetReady(); } GCMAppHandler* GCMDriver::GetAppHandler(const std::string& app_id) { - DCHECK(ui_thread_->RunsTasksOnCurrentThread()); - - std::map<std::string, GCMAppHandler*>::const_iterator iter = - app_handlers_.find(app_id); + GCMAppHandlerMap::const_iterator iter = app_handlers_.find(app_id); return iter == app_handlers_.end() ? &default_app_handler_ : iter->second; } -void GCMDriver::GetGCMStatisticsFinished(GCMClient::GCMStatistics stats) { - DCHECK(ui_thread_->RunsTasksOnCurrentThread()); - - // Normally request_gcm_statistics_callback_ would not be null. - if (!request_gcm_statistics_callback_.is_null()) - request_gcm_statistics_callback_.Run(stats); - else - LOG(WARNING) << "request_gcm_statistics_callback_ is NULL."; -} - -std::string GCMDriver::SignedInUserName() const { - if (IsStarted()) - return identity_provider_->GetActiveUsername(); - return std::string(); -} - } // namespace gcm diff --git a/components/gcm_driver/gcm_driver.h b/components/gcm_driver/gcm_driver.h index 897dbfa..367a6503 100644 --- a/components/gcm_driver/gcm_driver.h +++ b/components/gcm_driver/gcm_driver.h @@ -9,36 +9,18 @@ #include <string> #include <vector> -#include "base/basictypes.h" #include "base/callback.h" -#include "base/compiler_specific.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "base/memory/weak_ptr.h" +#include "base/macros.h" +#include "base/threading/thread_checker.h" #include "components/gcm_driver/default_gcm_app_handler.h" -#include "google_apis/gaia/identity_provider.h" #include "google_apis/gcm/gcm_client.h" -namespace base { -class FilePath; -class SequencedTaskRunner; -} - -namespace extensions { -class ExtensionGCMAppHandlerTest; -} - -namespace net { -class URLRequestContextGetter; -} - namespace gcm { class GCMAppHandler; -class GCMClientFactory; -// A bridge between the GCM users in Chrome and the GCMClient layer. -class GCMDriver : public IdentityProvider::Observer { +// Bridge between GCM users in Chrome and the platform-specific implementation. +class GCMDriver { public: typedef std::map<std::string, GCMAppHandler*> GCMAppHandlerMap; typedef base::Callback<void(const std::string& registration_id, @@ -49,21 +31,9 @@ class GCMDriver : public IdentityProvider::Observer { typedef base::Callback<void(const GCMClient::GCMStatistics& stats)> GetGCMStatisticsCallback; - GCMDriver( - scoped_ptr<GCMClientFactory> gcm_client_factory, - scoped_ptr<IdentityProvider> identity_provider, - const GCMClient::ChromeBuildInfo& chrome_build_info, - 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(); virtual ~GCMDriver(); - // Enables/disables GCM service. - void Enable(); - void Disable(); - // This method must be called before destroying the GCMDriver. Once it has // been called, no other GCMDriver methods may be used. virtual void Shutdown(); @@ -74,6 +44,10 @@ class GCMDriver : public IdentityProvider::Observer { // Remove the handler for a given app. virtual void RemoveAppHandler(const std::string& app_id); + // Enables/disables GCM service. + virtual void Enable() = 0; + virtual void Disable() = 0; + // Registers |sender_id| for an app. A registration ID will be returned by // the GCM server. // |app_id|: application ID. @@ -83,13 +57,13 @@ class GCMDriver : public IdentityProvider::Observer { // |callback|: to be called once the asynchronous operation is done. virtual void Register(const std::string& app_id, const std::vector<std::string>& sender_ids, - const RegisterCallback& callback); + const RegisterCallback& callback) = 0; // Unregisters an app from using GCM. // |app_id|: application ID. // |callback|: to be called once the asynchronous operation is done. virtual void Unregister(const std::string& app_id, - const UnregisterCallback& callback); + const UnregisterCallback& callback) = 0; // Sends a message to a given receiver. // |app_id|: application ID. @@ -99,110 +73,37 @@ class GCMDriver : public IdentityProvider::Observer { virtual void Send(const std::string& app_id, const std::string& receiver_id, const GCMClient::OutgoingMessage& message, - const SendCallback& callback); + const SendCallback& callback) = 0; - // For testing purpose. - GCMClient* GetGCMClientForTesting() const; + // For testing purpose. Always NULL on Android. + virtual GCMClient* GetGCMClientForTesting() const = 0; // Returns true if the service was started. - bool IsStarted() const; + virtual bool IsStarted() const = 0; // Returns true if the gcm client is ready. - bool IsGCMClientReady() const; + virtual bool IsGCMClientReady() const = 0; // Get GCM client internal states and statistics. // If clear_logs is true then activity logs will be cleared before the stats // are returned. - void GetGCMStatistics(const GetGCMStatisticsCallback& callback, - bool clear_logs); + virtual void GetGCMStatistics(const GetGCMStatisticsCallback& callback, + bool clear_logs) = 0; // Enables/disables GCM activity recording, and then returns the stats. - void SetGCMRecording(const GetGCMStatisticsCallback& callback, - bool recording); + virtual void SetGCMRecording(const GetGCMStatisticsCallback& callback, + bool recording) = 0; // Returns the user name if the profile is signed in. Empty string otherwise. - std::string SignedInUserName() const; - - // IdentityProvider::Observer: - virtual void OnActiveAccountLogin() OVERRIDE; - virtual void OnActiveAccountLogout() OVERRIDE; + virtual std::string SignedInUserName() const = 0; const GCMAppHandlerMap& app_handlers() const { return app_handlers_; } protected: - // Used for constructing fake GCMDriver for testing purpose. - GCMDriver(); - - private: - class DelayedTaskController; - class IOWorker; - - // Ensures that the GCM service starts when all of the following conditions - // satisfy: - // 1) GCM is enabled. - // 2) The identity provider is able to supply an account ID. - GCMClient::Result EnsureStarted(); - - // Stops the GCM service. It can be restarted by calling EnsureStarted again. - void Stop(); - - // Remove cached data when GCM service is stopped. - void RemoveCachedData(); - - // Checks out of GCM and erases any cached and persisted data. - void CheckOut(); - - // Should be called when an app with |app_id| is trying to un/register. - // Checks whether another un/registration is in progress. - bool IsAsyncOperationPending(const std::string& app_id) const; - - void DoRegister(const std::string& app_id, - const std::vector<std::string>& sender_ids); - void DoUnregister(const std::string& app_id); - void DoSend(const std::string& app_id, - const std::string& receiver_id, - const GCMClient::OutgoingMessage& message); - - // Callbacks posted from IO thread to UI thread. - void RegisterFinished(const std::string& app_id, - const std::string& registration_id, - GCMClient::Result result); - void UnregisterFinished(const std::string& app_id, GCMClient::Result result); - void SendFinished(const std::string& app_id, - const std::string& message_id, - GCMClient::Result result); - void MessageReceived(const std::string& app_id, - GCMClient::IncomingMessage message); - void MessagesDeleted(const std::string& app_id); - void MessageSendError(const std::string& app_id, - const GCMClient::SendErrorDetails& send_error_details); - void GCMClientReady(); - // Returns the handler for the given app. GCMAppHandler* GetAppHandler(const std::string& app_id); - void GetGCMStatisticsFinished(GCMClient::GCMStatistics stats); - - // Flag to indicate if GCM is enabled. - bool gcm_enabled_; - - // Flag to indicate if GCMClient is ready. - bool gcm_client_ready_; - - // The account ID that this service is responsible for. Empty when the service - // is not running. - std::string account_id_; - - scoped_ptr<IdentityProvider> identity_provider_; - scoped_refptr<base::SequencedTaskRunner> ui_thread_; - scoped_refptr<base::SequencedTaskRunner> io_thread_; - - scoped_ptr<DelayedTaskController> delayed_task_controller_; - - // For all the work occurring on the IO thread. Must be destroyed on the IO - // thread. - scoped_ptr<IOWorker> io_worker_; - + private: // App handler map (from app_id to handler pointer). // The handler is not owned. GCMAppHandlerMap app_handlers_; @@ -210,21 +111,6 @@ class GCMDriver : public IdentityProvider::Observer { // The default handler when no app handler can be found in the map. DefaultGCMAppHandler default_app_handler_; - // Callback map (from app_id to callback) for Register. - std::map<std::string, RegisterCallback> register_callbacks_; - - // Callback map (from app_id to callback) for Unregister. - std::map<std::string, UnregisterCallback> unregister_callbacks_; - - // Callback map (from <app_id, message_id> to callback) for Send. - std::map<std::pair<std::string, std::string>, SendCallback> send_callbacks_; - - // Callback for GetGCMStatistics. - GetGCMStatisticsCallback request_gcm_statistics_callback_; - - // Used to pass a weak pointer to the IO worker. - base::WeakPtrFactory<GCMDriver> weak_ptr_factory_; - DISALLOW_COPY_AND_ASSIGN(GCMDriver); }; diff --git a/components/gcm_driver/gcm_driver_android.cc b/components/gcm_driver/gcm_driver_android.cc index e7ae955..76f187e 100644 --- a/components/gcm_driver/gcm_driver_android.cc +++ b/components/gcm_driver/gcm_driver_android.cc @@ -5,6 +5,7 @@ #include "components/gcm_driver/gcm_driver_android.h" #include "base/compiler_specific.h" +#include "base/logging.h" namespace gcm { static void Java_GCMDriver_doNothing(JNIEnv* env) ALLOW_UNUSED; @@ -15,9 +16,69 @@ static void Java_GCMDriver_doNothing(JNIEnv* env) ALLOW_UNUSED; namespace gcm { +GCMDriverAndroid::GCMDriverAndroid() { +} + +GCMDriverAndroid::~GCMDriverAndroid() { +} + +void GCMDriverAndroid::Enable() { +} + +void GCMDriverAndroid::Disable() { +} + +void GCMDriverAndroid::Register(const std::string& app_id, + const std::vector<std::string>& sender_ids, + const RegisterCallback& callback) { + // TODO(johnme): Hook up to Android GCM API via JNI. + NOTIMPLEMENTED(); +} + +void GCMDriverAndroid::Unregister(const std::string& app_id, + const UnregisterCallback& callback) { + // TODO(johnme): Hook up to Android GCM API via JNI. + NOTIMPLEMENTED(); +} + +void GCMDriverAndroid::Send(const std::string& app_id, + const std::string& receiver_id, + const GCMClient::OutgoingMessage& message, + const SendCallback& callback) { + NOTIMPLEMENTED(); +} + +GCMClient* GCMDriverAndroid::GetGCMClientForTesting() const { + NOTIMPLEMENTED(); + return NULL; +} + +bool GCMDriverAndroid::IsStarted() const { + return true; +} + +bool GCMDriverAndroid::IsGCMClientReady() const { + return true; +} + +void GCMDriverAndroid::GetGCMStatistics( + const GetGCMStatisticsCallback& callback, + bool clear_logs) { + NOTIMPLEMENTED(); +} + +void GCMDriverAndroid::SetGCMRecording(const GetGCMStatisticsCallback& callback, + bool recording) { + NOTIMPLEMENTED(); +} + +std::string GCMDriverAndroid::SignedInUserName() const { + return std::string(); +} + // static bool GCMDriverAndroid::RegisterBindings(JNIEnv* env) { return RegisterNativesImpl(env); } -} // namespace gcm
\ No newline at end of file +} // namespace gcm diff --git a/components/gcm_driver/gcm_driver_android.h b/components/gcm_driver/gcm_driver_android.h index 653a4f2..0d0e876 100644 --- a/components/gcm_driver/gcm_driver_android.h +++ b/components/gcm_driver/gcm_driver_android.h @@ -7,14 +7,46 @@ #include <jni.h> +#include "base/compiler_specific.h" +#include "base/macros.h" +#include "components/gcm_driver/gcm_driver.h" + namespace gcm { -class GCMDriverAndroid { +// GCMDriver implementation for Android. +class GCMDriverAndroid : public GCMDriver { public: - // Register JNI methods + GCMDriverAndroid(); + virtual ~GCMDriverAndroid(); + + // GCMDriver implementation: + virtual void Enable() OVERRIDE; + virtual void Disable() OVERRIDE; + virtual void Register(const std::string& app_id, + const std::vector<std::string>& sender_ids, + const RegisterCallback& callback) OVERRIDE; + virtual void Unregister(const std::string& app_id, + const UnregisterCallback& callback) OVERRIDE; + virtual void Send(const std::string& app_id, + const std::string& receiver_id, + const GCMClient::OutgoingMessage& message, + const SendCallback& callback) OVERRIDE; + virtual GCMClient* GetGCMClientForTesting() const OVERRIDE; + virtual bool IsStarted() const OVERRIDE; + virtual bool IsGCMClientReady() const OVERRIDE; + virtual void GetGCMStatistics(const GetGCMStatisticsCallback& callback, + bool clear_logs) OVERRIDE; + virtual void SetGCMRecording(const GetGCMStatisticsCallback& callback, + bool recording) OVERRIDE; + virtual std::string SignedInUserName() const OVERRIDE; + + // Register JNI methods. static bool RegisterBindings(JNIEnv* env); + + private: + DISALLOW_COPY_AND_ASSIGN(GCMDriverAndroid); }; } // namespace gcm -#endif // COMPONENTS_GCM_DRIVER_GCM_DRIVER_ANDROID_H
\ No newline at end of file +#endif // COMPONENTS_GCM_DRIVER_GCM_DRIVER_ANDROID_H diff --git a/components/gcm_driver/gcm_driver_desktop.cc b/components/gcm_driver/gcm_driver_desktop.cc new file mode 100644 index 0000000..d57aa63 --- /dev/null +++ b/components/gcm_driver/gcm_driver_desktop.cc @@ -0,0 +1,809 @@ +// 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 <algorithm> +#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/sequenced_task_runner.h" +#include "base/threading/sequenced_worker_pool.h" +#include "components/gcm_driver/gcm_app_handler.h" +#include "components/gcm_driver/gcm_client_factory.h" +#include "components/gcm_driver/system_encryptor.h" +#include "google_apis/gaia/oauth2_token_service.h" +#include "net/url_request/url_request_context_getter.h" + +namespace gcm { + +// Helper class to save tasks to run until we're ready to execute them. +class GCMDriverDesktop::DelayedTaskController { + public: + DelayedTaskController(); + ~DelayedTaskController(); + + // Adds a task that will be invoked once we're ready. + void AddTask(const base::Closure& task); + + // Sets ready status. It is ready only when check-in is completed and + // the GCMClient is fully initialized. + void SetReady(); + + // Returns true if it is ready to perform tasks. + bool CanRunTaskWithoutDelay() const; + + private: + void RunTasks(); + + // Flag that indicates that GCM is ready. + bool ready_; + + std::vector<base::Closure> delayed_tasks_; + + DISALLOW_COPY_AND_ASSIGN(DelayedTaskController); +}; + +GCMDriverDesktop::DelayedTaskController::DelayedTaskController() + : ready_(false) { +} + +GCMDriverDesktop::DelayedTaskController::~DelayedTaskController() { +} + +void GCMDriverDesktop::DelayedTaskController::AddTask( + const base::Closure& task) { + delayed_tasks_.push_back(task); +} + +void GCMDriverDesktop::DelayedTaskController::SetReady() { + ready_ = true; + RunTasks(); +} + +bool GCMDriverDesktop::DelayedTaskController::CanRunTaskWithoutDelay() const { + return ready_; +} + +void GCMDriverDesktop::DelayedTaskController::RunTasks() { + DCHECK(ready_); + + for (size_t i = 0; i < delayed_tasks_.size(); ++i) + delayed_tasks_[i].Run(); + delayed_tasks_.clear(); +} + +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. + virtual void OnRegisterFinished(const std::string& app_id, + const std::string& registration_id, + GCMClient::Result result) OVERRIDE; + virtual void OnUnregisterFinished(const std::string& app_id, + GCMClient::Result result) OVERRIDE; + virtual void OnSendFinished(const std::string& app_id, + const std::string& message_id, + GCMClient::Result result) OVERRIDE; + virtual void OnMessageReceived( + const std::string& app_id, + const GCMClient::IncomingMessage& message) OVERRIDE; + virtual void OnMessagesDeleted(const std::string& app_id) OVERRIDE; + virtual void OnMessageSendError( + const std::string& app_id, + const GCMClient::SendErrorDetails& send_error_details) OVERRIDE; + virtual void OnGCMReady() OVERRIDE; + virtual void OnActivityRecorded() OVERRIDE; + + // Called on IO thread. + void Initialize( + scoped_ptr<GCMClientFactory> gcm_client_factory, + const GCMClient::ChromeBuildInfo& chrome_build_info, + const base::FilePath& store_path, + const std::vector<std::string>& account_ids, + const scoped_refptr<net::URLRequestContextGetter>& request_context, + const scoped_refptr<base::SequencedTaskRunner> blocking_task_runner); + void Start(const base::WeakPtr<GCMDriverDesktop>& service); + void Stop(); + void CheckOut(); + 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 GCMClient::OutgoingMessage& message); + void GetGCMStatistics(bool clear_logs); + void SetGCMRecording(bool recording); + + // 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_; + + scoped_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( + scoped_ptr<GCMClientFactory> gcm_client_factory, + const GCMClient::ChromeBuildInfo& chrome_build_info, + const base::FilePath& store_path, + const std::vector<std::string>& account_ids, + const scoped_refptr<net::URLRequestContextGetter>& request_context, + const scoped_refptr<base::SequencedTaskRunner> blocking_task_runner) { + DCHECK(io_thread_->RunsTasksOnCurrentThread()); + + gcm_client_ = gcm_client_factory->BuildInstance(); + + gcm_client_->Initialize(chrome_build_info, + store_path, + account_ids, + blocking_task_runner, + request_context, + make_scoped_ptr<Encryptor>(new SystemEncryptor), + this); +} + +void GCMDriverDesktop::IOWorker::OnRegisterFinished( + const std::string& app_id, + const std::string& registration_id, + GCMClient::Result result) { + DCHECK(io_thread_->RunsTasksOnCurrentThread()); + + ui_thread_->PostTask( + FROM_HERE, + base::Bind(&GCMDriverDesktop::RegisterFinished, service_, app_id, + registration_id, result)); +} + +void GCMDriverDesktop::IOWorker::OnUnregisterFinished( + const std::string& app_id, + GCMClient::Result result) { + DCHECK(io_thread_->RunsTasksOnCurrentThread()); + + ui_thread_->PostTask(FROM_HERE, + base::Bind(&GCMDriverDesktop::UnregisterFinished, + service_, + app_id, + 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 GCMClient::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::OnGCMReady() { + ui_thread_->PostTask( + FROM_HERE, + base::Bind(&GCMDriverDesktop::GCMClientReady, service_)); +} + +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(false); +} + +void GCMDriverDesktop::IOWorker::Start( + const base::WeakPtr<GCMDriverDesktop>& service) { + DCHECK(io_thread_->RunsTasksOnCurrentThread()); + + service_ = service; + gcm_client_->Start(); +} + +void GCMDriverDesktop::IOWorker::Stop() { + DCHECK(io_thread_->RunsTasksOnCurrentThread()); + + gcm_client_->Stop(); +} + +void GCMDriverDesktop::IOWorker::CheckOut() { + DCHECK(io_thread_->RunsTasksOnCurrentThread()); + + gcm_client_->CheckOut(); + + // Note that we still need to keep GCMClient instance alive since the + // GCMDriverDesktop may check in again. +} + +void GCMDriverDesktop::IOWorker::Register( + const std::string& app_id, + const std::vector<std::string>& sender_ids) { + DCHECK(io_thread_->RunsTasksOnCurrentThread()); + + gcm_client_->Register(app_id, sender_ids); +} + +void GCMDriverDesktop::IOWorker::Unregister(const std::string& app_id) { + DCHECK(io_thread_->RunsTasksOnCurrentThread()); + + gcm_client_->Unregister(app_id); +} + +void GCMDriverDesktop::IOWorker::Send( + const std::string& app_id, + const std::string& receiver_id, + const GCMClient::OutgoingMessage& message) { + DCHECK(io_thread_->RunsTasksOnCurrentThread()); + + gcm_client_->Send(app_id, receiver_id, message); +} + +void GCMDriverDesktop::IOWorker::GetGCMStatistics(bool clear_logs) { + DCHECK(io_thread_->RunsTasksOnCurrentThread()); + gcm::GCMClient::GCMStatistics stats; + + if (gcm_client_.get()) { + if (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)); +} + +GCMDriverDesktop::GCMDriverDesktop( + scoped_ptr<GCMClientFactory> gcm_client_factory, + scoped_ptr<IdentityProvider> identity_provider, + const GCMClient::ChromeBuildInfo& chrome_build_info, + 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) + : gcm_enabled_(true), + gcm_client_ready_(false), + identity_provider_(identity_provider.Pass()), + ui_thread_(ui_thread), + io_thread_(io_thread), + weak_ptr_factory_(this) { + // Get the list of available accounts. + std::vector<std::string> account_ids; + account_ids = identity_provider_->GetTokenService()->GetAccounts(); + + // 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, + account_ids, + request_context, + blocking_task_runner)); + + identity_provider_->AddObserver(this); +} + +GCMDriverDesktop::~GCMDriverDesktop() { +} + +void GCMDriverDesktop::Shutdown() { + DCHECK(ui_thread_->RunsTasksOnCurrentThread()); + identity_provider_->RemoveObserver(this); + GCMDriver::Shutdown(); + io_thread_->DeleteSoon(FROM_HERE, io_worker_.release()); +} + +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(); +} + +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. + if (app_handlers().empty()) + Stop(); +} + +void GCMDriverDesktop::Enable() { + DCHECK(ui_thread_->RunsTasksOnCurrentThread()); + + if (gcm_enabled_) + return; + gcm_enabled_ = true; + + EnsureStarted(); +} + +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 (account_id_.empty()) + return; + + RemoveCachedData(); + + io_thread_->PostTask( + FROM_HERE, + base::Bind(&GCMDriverDesktop::IOWorker::Stop, + base::Unretained(io_worker_.get()))); +} + +void GCMDriverDesktop::Register(const std::string& app_id, + const std::vector<std::string>& sender_ids, + const RegisterCallback& callback) { + DCHECK(ui_thread_->RunsTasksOnCurrentThread()); + DCHECK(!app_id.empty()); + DCHECK(!sender_ids.empty()); + DCHECK(!callback.is_null()); + + GCMClient::Result result = EnsureStarted(); + if (result != GCMClient::SUCCESS) { + callback.Run(std::string(), result); + return; + } + + // If previous un/register operation is still in progress, bail out. + if (IsAsyncOperationPending(app_id)) { + callback.Run(std::string(), GCMClient::ASYNC_OPERATION_PENDING); + return; + } + + register_callbacks_[app_id] = callback; + + // 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()); + std::map<std::string, RegisterCallback>::iterator callback_iter = + register_callbacks_.find(app_id); + if (callback_iter == register_callbacks_.end()) { + // The callback could have been removed when the app is uninstalled. + return; + } + + // Normalize the sender IDs by making them sorted. + std::vector<std::string> normalized_sender_ids = sender_ids; + std::sort(normalized_sender_ids.begin(), normalized_sender_ids.end()); + + io_thread_->PostTask( + FROM_HERE, + base::Bind(&GCMDriverDesktop::IOWorker::Register, + base::Unretained(io_worker_.get()), + app_id, + normalized_sender_ids)); +} + +void GCMDriverDesktop::Unregister(const std::string& app_id, + const UnregisterCallback& callback) { + DCHECK(ui_thread_->RunsTasksOnCurrentThread()); + DCHECK(!app_id.empty()); + DCHECK(!callback.is_null()); + + GCMClient::Result result = EnsureStarted(); + if (result != GCMClient::SUCCESS) { + callback.Run(result); + return; + } + + // If previous un/register operation is still in progress, bail out. + if (IsAsyncOperationPending(app_id)) { + callback.Run(GCMClient::ASYNC_OPERATION_PENDING); + return; + } + + unregister_callbacks_[app_id] = callback; + + // 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::Send(const std::string& app_id, + const std::string& receiver_id, + const GCMClient::OutgoingMessage& message, + const SendCallback& callback) { + DCHECK(ui_thread_->RunsTasksOnCurrentThread()); + DCHECK(!app_id.empty()); + DCHECK(!receiver_id.empty()); + DCHECK(!callback.is_null()); + + GCMClient::Result result = EnsureStarted(); + if (result != GCMClient::SUCCESS) { + callback.Run(std::string(), result); + return; + } + + // If the message with send ID is still in progress, bail out. + std::pair<std::string, std::string> key(app_id, message.id); + if (send_callbacks_.find(key) != send_callbacks_.end()) { + callback.Run(message.id, GCMClient::INVALID_PARAMETER); + return; + } + + send_callbacks_[key] = callback; + + // 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 GCMClient::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)); +} + +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 !account_id_.empty(); +} + +bool GCMDriverDesktop::IsGCMClientReady() const { + DCHECK(ui_thread_->RunsTasksOnCurrentThread()); + return gcm_client_ready_; +} + +void GCMDriverDesktop::GetGCMStatistics( + const GetGCMStatisticsCallback& callback, + bool 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::OnActiveAccountLogin() { + EnsureStarted(); +} + +void GCMDriverDesktop::OnActiveAccountLogout() { + CheckOut(); +} + +GCMClient::Result GCMDriverDesktop::EnsureStarted() { + DCHECK(ui_thread_->RunsTasksOnCurrentThread()); + + if (!gcm_enabled_) + return GCMClient::GCM_DISABLED; + + // Have any app requested the service? + if (app_handlers().empty()) + return GCMClient::UNKNOWN_ERROR; + + // Is the user signed in? + const std::string account_id = identity_provider_->GetActiveAccountId(); + if (account_id.empty()) + return GCMClient::NOT_SIGNED_IN; + + // CheckIn could be called more than once when: + // 1) The password changes. + // 2) Register/send function calls it to ensure CheckIn is done. + if (account_id_ == account_id) + return GCMClient::SUCCESS; + account_id_ = account_id; + + DCHECK(!delayed_task_controller_); + delayed_task_controller_.reset(new DelayedTaskController); + + // Note that we need to pass weak pointer again since the existing weak + // pointer in IOWorker might have been invalidated when check-out occurs. + io_thread_->PostTask( + FROM_HERE, + base::Bind(&GCMDriverDesktop::IOWorker::Start, + base::Unretained(io_worker_.get()), + 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(); + + account_id_.clear(); + gcm_client_ready_ = false; + delayed_task_controller_.reset(); + register_callbacks_.clear(); + send_callbacks_.clear(); +} + +void GCMDriverDesktop::CheckOut() { + DCHECK(ui_thread_->RunsTasksOnCurrentThread()); + + // We still proceed with the check-out logic even if the check-in is not + // initiated in the current session. This will make sure that all the + // persisted data written previously will get purged. + + RemoveCachedData(); + + io_thread_->PostTask( + FROM_HERE, + base::Bind(&GCMDriverDesktop::IOWorker::CheckOut, + base::Unretained(io_worker_.get()))); +} + +bool GCMDriverDesktop::IsAsyncOperationPending( + const std::string& app_id) const { + DCHECK(ui_thread_->RunsTasksOnCurrentThread()); + return register_callbacks_.find(app_id) != register_callbacks_.end() || + unregister_callbacks_.find(app_id) != unregister_callbacks_.end(); +} + +void GCMDriverDesktop::RegisterFinished(const std::string& app_id, + const std::string& registration_id, + GCMClient::Result result) { + DCHECK(ui_thread_->RunsTasksOnCurrentThread()); + + std::map<std::string, RegisterCallback>::iterator callback_iter = + register_callbacks_.find(app_id); + if (callback_iter == register_callbacks_.end()) { + // The callback could have been removed when the app is uninstalled. + return; + } + + RegisterCallback callback = callback_iter->second; + register_callbacks_.erase(callback_iter); + callback.Run(registration_id, result); +} + +void GCMDriverDesktop::UnregisterFinished(const std::string& app_id, + GCMClient::Result result) { + DCHECK(ui_thread_->RunsTasksOnCurrentThread()); + + std::map<std::string, UnregisterCallback>::iterator callback_iter = + unregister_callbacks_.find(app_id); + if (callback_iter == unregister_callbacks_.end()) + return; + + UnregisterCallback callback = callback_iter->second; + unregister_callbacks_.erase(callback_iter); + callback.Run(result); +} + +void GCMDriverDesktop::SendFinished(const std::string& app_id, + const std::string& message_id, + GCMClient::Result result) { + DCHECK(ui_thread_->RunsTasksOnCurrentThread()); + + std::map<std::pair<std::string, std::string>, SendCallback>::iterator + callback_iter = send_callbacks_.find( + std::pair<std::string, std::string>(app_id, message_id)); + if (callback_iter == send_callbacks_.end()) { + // The callback could have been removed when the app is uninstalled. + return; + } + + SendCallback callback = callback_iter->second; + send_callbacks_.erase(callback_iter); + callback.Run(message_id, result); +} + +void GCMDriverDesktop::MessageReceived(const std::string& app_id, + GCMClient::IncomingMessage message) { + DCHECK(ui_thread_->RunsTasksOnCurrentThread()); + + // Drop the event if signed out. + if (account_id_.empty()) + return; + + GetAppHandler(app_id)->OnMessage(app_id, message); +} + +void GCMDriverDesktop::MessagesDeleted(const std::string& app_id) { + DCHECK(ui_thread_->RunsTasksOnCurrentThread()); + + // Drop the event if signed out. + if (account_id_.empty()) + 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 signed out. + if (account_id_.empty()) + return; + + GetAppHandler(app_id)->OnSendError(app_id, send_error_details); +} + +void GCMDriverDesktop::GCMClientReady() { + DCHECK(ui_thread_->RunsTasksOnCurrentThread()); + + if (gcm_client_ready_) + return; + gcm_client_ready_ = true; + + delayed_task_controller_->SetReady(); +} + +void GCMDriverDesktop::GetGCMStatisticsFinished( + GCMClient::GCMStatistics stats) { + DCHECK(ui_thread_->RunsTasksOnCurrentThread()); + + // Normally request_gcm_statistics_callback_ would not be null. + if (!request_gcm_statistics_callback_.is_null()) + request_gcm_statistics_callback_.Run(stats); + else + LOG(WARNING) << "request_gcm_statistics_callback_ is NULL."; +} + +std::string GCMDriverDesktop::SignedInUserName() const { + if (IsStarted()) + return identity_provider_->GetActiveUsername(); + return std::string(); +} + +} // namespace gcm diff --git a/components/gcm_driver/gcm_driver_desktop.h b/components/gcm_driver/gcm_driver_desktop.h new file mode 100644 index 0000000..1a6d882 --- /dev/null +++ b/components/gcm_driver/gcm_driver_desktop.h @@ -0,0 +1,171 @@ +// 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 COMPONENTS_GCM_DRIVER_GCM_DRIVER_DESKTOP_H_ +#define COMPONENTS_GCM_DRIVER_GCM_DRIVER_DESKTOP_H_ + +#include <map> +#include <string> +#include <vector> + +#include "base/compiler_specific.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "components/gcm_driver/gcm_driver.h" +#include "google_apis/gaia/identity_provider.h" +#include "google_apis/gcm/gcm_client.h" + +namespace base { +class FilePath; +class SequencedTaskRunner; +} + +namespace extensions { +class ExtensionGCMAppHandlerTest; +} + +namespace net { +class URLRequestContextGetter; +} + +namespace gcm { + +class GCMAppHandler; +class GCMClientFactory; + +// GCMDriver implementation for desktop and Chrome OS, using GCMClient. +class GCMDriverDesktop : public GCMDriver, public IdentityProvider::Observer { + public: + GCMDriverDesktop( + scoped_ptr<GCMClientFactory> gcm_client_factory, + scoped_ptr<IdentityProvider> identity_provider, + const GCMClient::ChromeBuildInfo& chrome_build_info, + 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); + virtual ~GCMDriverDesktop(); + + // GCMDriver overrides: + virtual void Shutdown() OVERRIDE; + virtual void AddAppHandler(const std::string& app_id, + GCMAppHandler* handler) OVERRIDE; + virtual void RemoveAppHandler(const std::string& app_id) OVERRIDE; + + // GCMDriver implementation: + virtual void Enable() OVERRIDE; + virtual void Disable() OVERRIDE; + virtual void Register(const std::string& app_id, + const std::vector<std::string>& sender_ids, + const RegisterCallback& callback) OVERRIDE; + virtual void Unregister(const std::string& app_id, + const UnregisterCallback& callback) OVERRIDE; + virtual void Send(const std::string& app_id, + const std::string& receiver_id, + const GCMClient::OutgoingMessage& message, + const SendCallback& callback) OVERRIDE; + virtual GCMClient* GetGCMClientForTesting() const OVERRIDE; + virtual bool IsStarted() const OVERRIDE; + virtual bool IsGCMClientReady() const OVERRIDE; + virtual void GetGCMStatistics(const GetGCMStatisticsCallback& callback, + bool clear_logs) OVERRIDE; + virtual void SetGCMRecording(const GetGCMStatisticsCallback& callback, + bool recording) OVERRIDE; + virtual std::string SignedInUserName() const OVERRIDE; + + // IdentityProvider::Observer implementation: + virtual void OnActiveAccountLogin() OVERRIDE; + virtual void OnActiveAccountLogout() OVERRIDE; + + private: + class DelayedTaskController; + class IOWorker; + + // Ensures that the GCM service starts when all of the following conditions + // satisfy: + // 1) GCM is enabled. + // 2) The identity provider is able to supply an account ID. + GCMClient::Result EnsureStarted(); + + // Stops the GCM service. It can be restarted by calling EnsureStarted again. + void Stop(); + + // Remove cached data when GCM service is stopped. + void RemoveCachedData(); + + // Checks out of GCM and erases any cached and persisted data. + void CheckOut(); + + // Should be called when an app with |app_id| is trying to un/register. + // Checks whether another un/registration is in progress. + bool IsAsyncOperationPending(const std::string& app_id) const; + + void DoRegister(const std::string& app_id, + const std::vector<std::string>& sender_ids); + void DoUnregister(const std::string& app_id); + void DoSend(const std::string& app_id, + const std::string& receiver_id, + const GCMClient::OutgoingMessage& message); + + // Callbacks posted from IO thread to UI thread. + void RegisterFinished(const std::string& app_id, + const std::string& registration_id, + GCMClient::Result result); + void UnregisterFinished(const std::string& app_id, GCMClient::Result result); + void SendFinished(const std::string& app_id, + const std::string& message_id, + GCMClient::Result result); + void MessageReceived(const std::string& app_id, + GCMClient::IncomingMessage message); + void MessagesDeleted(const std::string& app_id); + void MessageSendError(const std::string& app_id, + const GCMClient::SendErrorDetails& send_error_details); + void GCMClientReady(); + + void GetGCMStatisticsFinished(GCMClient::GCMStatistics stats); + + // Flag to indicate if GCM is enabled. + bool gcm_enabled_; + + // Flag to indicate if GCMClient is ready. + bool gcm_client_ready_; + + // The account ID that this service is responsible for. Empty when the service + // is not running. + std::string account_id_; + + scoped_ptr<IdentityProvider> identity_provider_; + scoped_refptr<base::SequencedTaskRunner> ui_thread_; + scoped_refptr<base::SequencedTaskRunner> io_thread_; + + scoped_ptr<DelayedTaskController> delayed_task_controller_; + + // For all the work occurring on the IO thread. Must be destroyed on the IO + // thread. + scoped_ptr<IOWorker> io_worker_; + + // Callback map (from app_id to callback) for Register. + std::map<std::string, RegisterCallback> register_callbacks_; + + // Callback map (from app_id to callback) for Unregister. + std::map<std::string, UnregisterCallback> unregister_callbacks_; + + // Callback map (from <app_id, message_id> to callback) for Send. + std::map<std::pair<std::string, std::string>, SendCallback> send_callbacks_; + + // Callback for GetGCMStatistics. + GetGCMStatisticsCallback request_gcm_statistics_callback_; + + // Used to pass a weak pointer to the IO worker. + base::WeakPtrFactory<GCMDriverDesktop> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(GCMDriverDesktop); +}; + +} // namespace gcm + +#endif // COMPONENTS_GCM_DRIVER_GCM_DRIVER_DESKTOP_H_ diff --git a/components/gcm_driver/gcm_driver_unittest.cc b/components/gcm_driver/gcm_driver_desktop_unittest.cc index 52f6abb..d4694cfb 100644 --- a/components/gcm_driver/gcm_driver_unittest.cc +++ b/components/gcm_driver/gcm_driver_desktop_unittest.cc @@ -2,7 +2,7 @@ // 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.h" +#include "components/gcm_driver/gcm_driver_desktop.h" #include "base/bind.h" #include "base/bind_helpers.h" @@ -190,7 +190,8 @@ void GCMDriverTest::CreateDriver( FakeGCMClient::StartMode gcm_client_start_mode) { scoped_refptr<net::URLRequestContextGetter> request_context = new net::TestURLRequestContextGetter(io_thread_.message_loop_proxy()); - driver_.reset(new GCMDriver( + // TODO(johnme): Need equivalent test coverage of GCMDriverAndroid. + driver_.reset(new GCMDriverDesktop( scoped_ptr<GCMClientFactory>(new FakeGCMClientFactory( gcm_client_start_mode, base::MessageLoopProxy::current(), |