summaryrefslogtreecommitdiffstats
path: root/components
diff options
context:
space:
mode:
authorjohnme@chromium.org <johnme@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-31 01:21:09 +0000
committerjohnme@chromium.org <johnme@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-31 01:21:09 +0000
commit21b7765ae5142044dd95ce042ad93f9f2b963b17 (patch)
treee00da04bc07b7033b74b83bf7f76a1954ef1eddd /components
parent631fc091a7db836843ddd7e15a847068d72e43b4 (diff)
downloadchromium_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.gyp3
-rw-r--r--components/gcm_driver.gypi8
-rw-r--r--components/gcm_driver/fake_gcm_driver.cc70
-rw-r--r--components/gcm_driver/fake_gcm_driver.h52
-rw-r--r--components/gcm_driver/gcm_driver.cc781
-rw-r--r--components/gcm_driver/gcm_driver.h158
-rw-r--r--components/gcm_driver/gcm_driver_android.cc63
-rw-r--r--components/gcm_driver/gcm_driver_android.h38
-rw-r--r--components/gcm_driver/gcm_driver_desktop.cc809
-rw-r--r--components/gcm_driver/gcm_driver_desktop.h171
-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(),