summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbartfab@chromium.org <bartfab@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-04-25 15:36:54 +0000
committerbartfab@chromium.org <bartfab@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-04-25 15:36:54 +0000
commitdf84c5378331d859148ad916a706fe2d9be441fd (patch)
treed18a4d4502bb65cadc8c310e471f5b9e0c00bd7c
parent671d33072bfcc49a6aeac7559d349e7bc0d994e9 (diff)
downloadchromium_src-df84c5378331d859148ad916a706fe2d9be441fd.zip
chromium_src-df84c5378331d859148ad916a706fe2d9be441fd.tar.gz
chromium_src-df84c5378331d859148ad916a706fe2d9be441fd.tar.bz2
Extract Profile-independent GCMService from GCMProfileService
This CL moves most of the GCMProfileService functionality to a new GCMService class that does not depend on Profile. GCMProfileService becomes a subclass of GCMService that adds Profile-specific lifetime management and control over the service via user preferences. The CL is a prerequisite for Chrome OS device policy pushing, which will need to instantiate a Tango connection using device-wide GAIA credentials not tied to any user or Profile. The CL also fixes a few subtle bugs, such as GCMProfileService::IOWorker being shut down on the wrong thread and unit tests looking at the wrong GCMProfileService when trying to verify correct behavior. BUG=362083 TEST=Updated unit tests R=atwilson@chromium.org, dcheng@chromium.org, jianli@chromium.org, yoz@chromium.org Review URL: https://codereview.chromium.org/225403021 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@266197 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/extensions/extension_gcm_app_handler.h6
-rw-r--r--chrome/browser/extensions/extension_gcm_app_handler_unittest.cc149
-rw-r--r--chrome/browser/invalidation/gcm_invalidation_bridge.cc25
-rw-r--r--chrome/browser/invalidation/gcm_invalidation_bridge.h6
-rw-r--r--chrome/browser/invalidation/ticl_invalidation_service.cc5
-rw-r--r--chrome/browser/services/gcm/fake_gcm_client_factory.cc24
-rw-r--r--chrome/browser/services/gcm/fake_gcm_client_factory.h34
-rw-r--r--chrome/browser/services/gcm/fake_signin_manager.cc56
-rw-r--r--chrome/browser/services/gcm/fake_signin_manager.h54
-rw-r--r--chrome/browser/services/gcm/gcm_client_mock.cc2
-rw-r--r--chrome/browser/services/gcm/gcm_profile_service.cc908
-rw-r--r--chrome/browser/services/gcm/gcm_profile_service.h203
-rw-r--r--chrome/browser/services/gcm/gcm_profile_service_factory.cc10
-rw-r--r--chrome/browser/services/gcm/gcm_profile_service_factory.h3
-rw-r--r--chrome/browser/services/gcm/gcm_profile_service_test_helper.cc119
-rw-r--r--chrome/browser/services/gcm/gcm_profile_service_test_helper.h94
-rw-r--r--chrome/browser/services/gcm/gcm_profile_service_unittest.cc1269
-rw-r--r--chrome/browser/services/gcm/gcm_service.cc850
-rw-r--r--chrome/browser/services/gcm/gcm_service.h217
-rw-r--r--chrome/browser/services/gcm/gcm_service_unittest.cc1318
-rw-r--r--chrome/browser/signin/profile_identity_provider.cc9
-rw-r--r--chrome/chrome_browser.gypi2
-rw-r--r--chrome/chrome_tests_unit.gypi7
-rw-r--r--sync/notifier/gcm_network_channel.h2
-rw-r--r--sync/notifier/gcm_network_channel_delegate.h4
25 files changed, 2883 insertions, 2493 deletions
diff --git a/chrome/browser/extensions/extension_gcm_app_handler.h b/chrome/browser/extensions/extension_gcm_app_handler.h
index 8e90ddf..9a7298d 100644
--- a/chrome/browser/extensions/extension_gcm_app_handler.h
+++ b/chrome/browser/extensions/extension_gcm_app_handler.h
@@ -5,6 +5,10 @@
#ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_GCM_APP_HANDLER_H_
#define CHROME_BROWSER_EXTENSIONS_EXTENSION_GCM_APP_HANDLER_H_
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_observer.h"
@@ -16,9 +20,11 @@
#include "google_apis/gcm/gcm_client.h"
class Profile;
+
namespace content {
class BrowserContext;
}
+
namespace gcm {
class GCMProfileService;
}
diff --git a/chrome/browser/extensions/extension_gcm_app_handler_unittest.cc b/chrome/browser/extensions/extension_gcm_app_handler_unittest.cc
index 1adc3dc..6b57261 100644
--- a/chrome/browser/extensions/extension_gcm_app_handler_unittest.cc
+++ b/chrome/browser/extensions/extension_gcm_app_handler_unittest.cc
@@ -2,35 +2,54 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "chrome/browser/extensions/extension_gcm_app_handler.h"
+
+#include <vector>
+
#include "base/bind.h"
+#include "base/bind_helpers.h"
#include "base/command_line.h"
-#include "base/prefs/pref_service.h"
-#include "chrome/browser/extensions/extension_gcm_app_handler.h"
+#include "base/files/file_path.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/values.h"
+#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/test_extension_service.h"
#include "chrome/browser/extensions/test_extension_system.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/services/gcm/fake_gcm_client_factory.h"
+#include "chrome/browser/services/gcm/fake_signin_manager.h"
+#include "chrome/browser/services/gcm/gcm_client_factory.h"
#include "chrome/browser/services/gcm/gcm_client_mock.h"
#include "chrome/browser/services/gcm/gcm_profile_service.h"
#include "chrome/browser/services/gcm/gcm_profile_service_factory.h"
-#include "chrome/browser/services/gcm/gcm_profile_service_test_helper.h"
#include "chrome/browser/signin/signin_manager_factory.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/testing_profile.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/test_browser_thread_bundle.h"
+#include "extensions/browser/extension_system.h"
#include "extensions/common/extension.h"
+#include "extensions/common/manifest.h"
#include "extensions/common/manifest_constants.h"
+#include "extensions/common/permissions/api_permission.h"
#include "testing/gtest/include/gtest/gtest.h"
+#if !defined(OS_ANDROID)
+#include "chrome/browser/extensions/api/gcm/gcm_api.h"
+#endif
+
#if defined(OS_CHROMEOS)
#include "chrome/browser/chromeos/login/user_manager.h"
#include "chrome/browser/chromeos/settings/cros_settings.h"
#include "chrome/browser/chromeos/settings/device_settings_service.h"
-#else
-#include "components/signin/core/browser/signin_manager.h"
#endif
-using namespace gcm;
-
namespace extensions {
namespace {
@@ -40,12 +59,75 @@ const char kTestingUsername[] = "user1@example.com";
} // namespace
+// Helper class for asynchronous waiting.
+class Waiter {
+ public:
+ Waiter() {}
+ ~Waiter() {}
+
+ // Waits until the asynchronous operation finishes.
+ void WaitUntilCompleted() {
+ run_loop_.reset(new base::RunLoop);
+ run_loop_->Run();
+ }
+
+ // Signals that the asynchronous operation finishes.
+ void SignalCompleted() {
+ if (run_loop_ && run_loop_->running())
+ run_loop_->Quit();
+ }
+
+ // Runs until UI loop becomes idle.
+ void PumpUILoop() {
+ base::MessageLoop::current()->RunUntilIdle();
+ }
+
+ // Runs until IO loop becomes idle.
+ void PumpIOLoop() {
+ content::BrowserThread::PostTask(
+ content::BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&Waiter::OnIOLoopPump, base::Unretained(this)));
+
+ WaitUntilCompleted();
+ }
+
+ private:
+ void PumpIOLoopCompleted() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+ SignalCompleted();
+ }
+
+ void OnIOLoopPump() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+
+ content::BrowserThread::PostTask(
+ content::BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&Waiter::OnIOLoopPumpCompleted, base::Unretained(this)));
+ }
+
+ void OnIOLoopPumpCompleted() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+
+ content::BrowserThread::PostTask(
+ content::BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&Waiter::PumpIOLoopCompleted, base::Unretained(this)));
+ }
+
+ scoped_ptr<base::RunLoop> run_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(Waiter);
+};
+
class FakeExtensionGCMAppHandler : public ExtensionGCMAppHandler {
public:
FakeExtensionGCMAppHandler(Profile* profile, Waiter* waiter)
: ExtensionGCMAppHandler(profile),
waiter_(waiter),
- unregistration_result_(GCMClient::UNKNOWN_ERROR) {
+ unregistration_result_(gcm::GCMClient::UNKNOWN_ERROR) {
}
virtual ~FakeExtensionGCMAppHandler() {
@@ -53,7 +135,7 @@ class FakeExtensionGCMAppHandler : public ExtensionGCMAppHandler {
virtual void OnMessage(
const std::string& app_id,
- const GCMClient::IncomingMessage& message)OVERRIDE {
+ const gcm::GCMClient::IncomingMessage& message) OVERRIDE {
}
virtual void OnMessagesDeleted(const std::string& app_id) OVERRIDE {
@@ -61,22 +143,22 @@ class FakeExtensionGCMAppHandler : public ExtensionGCMAppHandler {
virtual void OnSendError(
const std::string& app_id,
- const GCMClient::SendErrorDetails& send_error_details) OVERRIDE {
+ const gcm::GCMClient::SendErrorDetails& send_error_details) OVERRIDE {
}
virtual void OnUnregisterCompleted(const std::string& app_id,
- GCMClient::Result result) OVERRIDE {
+ gcm::GCMClient::Result result) OVERRIDE {
unregistration_result_ = result;
waiter_->SignalCompleted();
}
- GCMClient::Result unregistration_result() const {
+ gcm::GCMClient::Result unregistration_result() const {
return unregistration_result_;
}
private:
Waiter* waiter_;
- GCMClient::Result unregistration_result_;
+ gcm::GCMClient::Result unregistration_result_;
DISALLOW_COPY_AND_ASSIGN(FakeExtensionGCMAppHandler);
};
@@ -85,13 +167,13 @@ class ExtensionGCMAppHandlerTest : public testing::Test {
public:
static KeyedService* BuildGCMProfileService(
content::BrowserContext* context) {
- return new GCMProfileService(static_cast<Profile*>(context));
+ return new gcm::GCMProfileService(static_cast<Profile*>(context));
}
ExtensionGCMAppHandlerTest()
: extension_service_(NULL),
- registration_result_(GCMClient::UNKNOWN_ERROR),
- unregistration_result_(GCMClient::UNKNOWN_ERROR) {
+ registration_result_(gcm::GCMClient::UNKNOWN_ERROR),
+ unregistration_result_(gcm::GCMClient::UNKNOWN_ERROR) {
}
virtual ~ExtensionGCMAppHandlerTest() {
@@ -127,12 +209,14 @@ class ExtensionGCMAppHandlerTest : public testing::Test {
profile()->GetPrefs()->SetBoolean(prefs::kGCMChannelEnabled, true);
// Create GCMProfileService that talks with fake GCMClient.
- GCMProfileService* gcm_profile_service = static_cast<GCMProfileService*>(
- GCMProfileServiceFactory::GetInstance()->SetTestingFactoryAndUse(
- profile(),
- &ExtensionGCMAppHandlerTest::BuildGCMProfileService));
- scoped_ptr<GCMClientFactory> gcm_client_factory(
- new FakeGCMClientFactory(GCMClientMock::NO_DELAY_LOADING));
+ gcm::GCMProfileService* gcm_profile_service =
+ static_cast<gcm::GCMProfileService*>(
+ gcm::GCMProfileServiceFactory::GetInstance()->
+ SetTestingFactoryAndUse(
+ profile(),
+ &ExtensionGCMAppHandlerTest::BuildGCMProfileService));
+ scoped_ptr<gcm::GCMClientFactory> gcm_client_factory(
+ new gcm::FakeGCMClientFactory(gcm::GCMClientMock::NO_DELAY_LOADING));
gcm_profile_service->Initialize(gcm_client_factory.Pass());
// Create a fake version of ExtensionGCMAppHandler.
@@ -212,13 +296,13 @@ class ExtensionGCMAppHandlerTest : public testing::Test {
}
void RegisterCompleted(const std::string& registration_id,
- GCMClient::Result result) {
+ gcm::GCMClient::Result result) {
registration_result_ = result;
waiter_.SignalCompleted();
}
- GCMProfileService* GetGCMProfileService() const {
- return GCMProfileServiceFactory::GetForProfile(profile());
+ gcm::GCMProfileService* GetGCMProfileService() const {
+ return gcm::GCMProfileServiceFactory::GetForProfile(profile());
}
bool HasAppHandlers(const std::string& app_id) const {
@@ -230,8 +314,10 @@ class ExtensionGCMAppHandlerTest : public testing::Test {
FakeExtensionGCMAppHandler* gcm_app_handler() const {
return gcm_app_handler_.get();
}
- GCMClient::Result registration_result() const { return registration_result_; }
- GCMClient::Result unregistration_result() const {
+ gcm::GCMClient::Result registration_result() const {
+ return registration_result_;
+ }
+ gcm::GCMClient::Result unregistration_result() const {
return unregistration_result_;
}
@@ -250,8 +336,8 @@ class ExtensionGCMAppHandlerTest : public testing::Test {
Waiter waiter_;
scoped_ptr<FakeExtensionGCMAppHandler> gcm_app_handler_;
- GCMClient::Result registration_result_;
- GCMClient::Result unregistration_result_;
+ gcm::GCMClient::Result registration_result_;
+ gcm::GCMClient::Result unregistration_result_;
DISALLOW_COPY_AND_ASSIGN(ExtensionGCMAppHandlerTest);
};
@@ -292,12 +378,13 @@ TEST_F(ExtensionGCMAppHandlerTest, UnregisterOnExtensionUninstall) {
sender_ids.push_back("sender1");
Register(extension->id(), sender_ids);
waiter()->WaitUntilCompleted();
- EXPECT_EQ(GCMClient::SUCCESS, registration_result());
+ EXPECT_EQ(gcm::GCMClient::SUCCESS, registration_result());
// Unregistration should be triggered when the extension is uninstalled.
UninstallExtension(extension);
waiter()->WaitUntilCompleted();
- EXPECT_EQ(GCMClient::SUCCESS, gcm_app_handler()->unregistration_result());
+ EXPECT_EQ(gcm::GCMClient::SUCCESS,
+ gcm_app_handler()->unregistration_result());
}
} // namespace extensions
diff --git a/chrome/browser/invalidation/gcm_invalidation_bridge.cc b/chrome/browser/invalidation/gcm_invalidation_bridge.cc
index 5925eda..88d0971 100644
--- a/chrome/browser/invalidation/gcm_invalidation_bridge.cc
+++ b/chrome/browser/invalidation/gcm_invalidation_bridge.cc
@@ -7,7 +7,7 @@
#include "base/single_thread_task_runner.h"
#include "base/thread_task_runner_handle.h"
#include "chrome/browser/invalidation/gcm_invalidation_bridge.h"
-#include "chrome/browser/services/gcm/gcm_profile_service.h"
+#include "chrome/browser/services/gcm/gcm_service.h"
#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
#include "chrome/browser/signin/signin_manager_factory.h"
#include "components/signin/core/browser/profile_oauth2_token_service.h"
@@ -148,17 +148,17 @@ void GCMInvalidationBridge::Core::OnIncomingMessage(
}
GCMInvalidationBridge::GCMInvalidationBridge(
- gcm::GCMProfileService* gcm_profile_service,
+ gcm::GCMService* gcm_service,
IdentityProvider* identity_provider)
: OAuth2TokenService::Consumer("gcm_network_channel"),
- gcm_profile_service_(gcm_profile_service),
+ gcm_service_(gcm_service),
identity_provider_(identity_provider),
subscribed_for_incoming_messages_(false),
weak_factory_(this) {}
GCMInvalidationBridge::~GCMInvalidationBridge() {
if (subscribed_for_incoming_messages_)
- gcm_profile_service_->RemoveAppHandler(kInvalidationsAppId);
+ gcm_service_->RemoveAppHandler(kInvalidationsAppId);
}
scoped_ptr<syncer::GCMNetworkChannelDelegate>
@@ -244,17 +244,16 @@ void GCMInvalidationBridge::Register(
syncer::GCMNetworkChannelDelegate::RegisterCallback callback) {
DCHECK(CalledOnValidThread());
// No-op if GCMClient is disabled.
- if (gcm_profile_service_ == NULL)
+ if (gcm_service_ == NULL)
return;
std::vector<std::string> sender_ids;
sender_ids.push_back(kInvalidationsSenderId);
- gcm_profile_service_->Register(
- kInvalidationsAppId,
- sender_ids,
- base::Bind(&GCMInvalidationBridge::RegisterFinished,
- weak_factory_.GetWeakPtr(),
- callback));
+ gcm_service_->Register(kInvalidationsAppId,
+ sender_ids,
+ base::Bind(&GCMInvalidationBridge::RegisterFinished,
+ weak_factory_.GetWeakPtr(),
+ callback));
}
void GCMInvalidationBridge::RegisterFinished(
@@ -273,11 +272,11 @@ void GCMInvalidationBridge::RegisterFinished(
void GCMInvalidationBridge::SubscribeForIncomingMessages() {
// No-op if GCMClient is disabled.
- if (gcm_profile_service_ == NULL)
+ if (gcm_service_ == NULL)
return;
DCHECK(!subscribed_for_incoming_messages_);
- gcm_profile_service_->AddAppHandler(kInvalidationsAppId, this);
+ gcm_service_->AddAppHandler(kInvalidationsAppId, this);
subscribed_for_incoming_messages_ = true;
}
diff --git a/chrome/browser/invalidation/gcm_invalidation_bridge.h b/chrome/browser/invalidation/gcm_invalidation_bridge.h
index c72c6a1..a9d781e 100644
--- a/chrome/browser/invalidation/gcm_invalidation_bridge.h
+++ b/chrome/browser/invalidation/gcm_invalidation_bridge.h
@@ -21,7 +21,7 @@ class SingleThreadTaskRunner;
} // namespace base
namespace gcm {
-class GCMProfileService;
+class GCMService;
} // namespace gcm
namespace invalidation {
@@ -37,7 +37,7 @@ class GCMInvalidationBridge : public gcm::GCMAppHandler,
public:
class Core;
- GCMInvalidationBridge(gcm::GCMProfileService* gcm_profile_service,
+ GCMInvalidationBridge(gcm::GCMService* gcm_service,
IdentityProvider* identity_provider);
virtual ~GCMInvalidationBridge();
@@ -80,7 +80,7 @@ class GCMInvalidationBridge : public gcm::GCMAppHandler,
gcm::GCMClient::Result result);
private:
- gcm::GCMProfileService* const gcm_profile_service_;
+ gcm::GCMService* const gcm_service_;
IdentityProvider* const identity_provider_;
base::WeakPtr<Core> core_;
diff --git a/chrome/browser/invalidation/ticl_invalidation_service.cc b/chrome/browser/invalidation/ticl_invalidation_service.cc
index 8db9111..3eecec9 100644
--- a/chrome/browser/invalidation/ticl_invalidation_service.cc
+++ b/chrome/browser/invalidation/ticl_invalidation_service.cc
@@ -12,6 +12,7 @@
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/services/gcm/gcm_profile_service.h"
#include "chrome/browser/services/gcm/gcm_profile_service_factory.h"
+#include "chrome/browser/services/gcm/gcm_service.h"
#include "chrome/common/chrome_content_client.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
@@ -394,10 +395,10 @@ void TiclInvalidationService::StartInvalidator(
break;
}
case GCM_NETWORK_CHANNEL: {
- gcm::GCMProfileService* gcm_profile_service =
+ gcm::GCMService* gcm_service =
gcm::GCMProfileServiceFactory::GetForProfile(profile_);
gcm_invalidation_bridge_.reset(new GCMInvalidationBridge(
- gcm_profile_service, identity_provider_.get()));
+ gcm_service, identity_provider_.get()));
network_channel_creator =
syncer::NonBlockingInvalidator::MakeGCMNetworkChannelCreator(
request_context_,
diff --git a/chrome/browser/services/gcm/fake_gcm_client_factory.cc b/chrome/browser/services/gcm/fake_gcm_client_factory.cc
new file mode 100644
index 0000000..2d1d23c
--- /dev/null
+++ b/chrome/browser/services/gcm/fake_gcm_client_factory.cc
@@ -0,0 +1,24 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/services/gcm/fake_gcm_client_factory.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "google_apis/gcm/gcm_client.h"
+
+namespace gcm {
+
+FakeGCMClientFactory::FakeGCMClientFactory(
+ GCMClientMock::LoadingDelay gcm_client_loading_delay)
+ : gcm_client_loading_delay_(gcm_client_loading_delay) {
+}
+
+FakeGCMClientFactory::~FakeGCMClientFactory() {
+}
+
+scoped_ptr<GCMClient> FakeGCMClientFactory::BuildInstance() {
+ return scoped_ptr<GCMClient>(new GCMClientMock(gcm_client_loading_delay_));
+}
+
+} // namespace gcm
diff --git a/chrome/browser/services/gcm/fake_gcm_client_factory.h b/chrome/browser/services/gcm/fake_gcm_client_factory.h
new file mode 100644
index 0000000..ba6c74a
--- /dev/null
+++ b/chrome/browser/services/gcm/fake_gcm_client_factory.h
@@ -0,0 +1,34 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SERVICES_GCM_FAKE_GCM_CLIENT_FACTORY_H_
+#define CHROME_BROWSER_SERVICES_GCM_FAKE_GCM_CLIENT_FACTORY_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "chrome/browser/services/gcm/gcm_client_factory.h"
+#include "chrome/browser/services/gcm/gcm_client_mock.h"
+
+namespace gcm {
+
+class GCMClient;
+
+class FakeGCMClientFactory : public GCMClientFactory {
+ public:
+ explicit FakeGCMClientFactory(
+ GCMClientMock::LoadingDelay gcm_client_loading_delay);
+ virtual ~FakeGCMClientFactory();
+
+ // GCMClientFactory:
+ virtual scoped_ptr<GCMClient> BuildInstance() OVERRIDE;
+
+ private:
+ GCMClientMock::LoadingDelay gcm_client_loading_delay_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeGCMClientFactory);
+};
+
+} // namespace gcm
+
+#endif // CHROME_BROWSER_SERVICES_GCM_FAKE_GCM_CLIENT_FACTORY_H_
diff --git a/chrome/browser/services/gcm/fake_signin_manager.cc b/chrome/browser/services/gcm/fake_signin_manager.cc
new file mode 100644
index 0000000..967c0b9
--- /dev/null
+++ b/chrome/browser/services/gcm/fake_signin_manager.cc
@@ -0,0 +1,56 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/services/gcm/fake_signin_manager.h"
+
+#include "base/observer_list.h"
+#include "base/prefs/pref_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/chrome_signin_client_factory.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "components/signin/core/common/signin_pref_names.h"
+#include "content/public/browser/browser_context.h"
+
+#if !defined(OS_CHROMEOS)
+#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
+#endif
+
+namespace gcm {
+
+FakeSigninManager::FakeSigninManager(Profile* profile)
+#if defined(OS_CHROMEOS)
+ : SigninManagerBase(
+ ChromeSigninClientFactory::GetInstance()->GetForProfile(profile)),
+#else
+ : SigninManager(
+ ChromeSigninClientFactory::GetInstance()->GetForProfile(profile),
+ ProfileOAuth2TokenServiceFactory::GetForProfile(profile)),
+#endif
+ profile_(profile) {
+ Initialize(NULL);
+}
+
+FakeSigninManager::~FakeSigninManager() {
+}
+
+void FakeSigninManager::SignIn(const std::string& username) {
+ SetAuthenticatedUsername(username);
+ FOR_EACH_OBSERVER(Observer,
+ observer_list_,
+ GoogleSigninSucceeded(username, std::string()));
+}
+
+void FakeSigninManager::SignOut() {
+ const std::string username = GetAuthenticatedUsername();
+ clear_authenticated_username();
+ profile_->GetPrefs()->ClearPref(prefs::kGoogleServicesUsername);
+ FOR_EACH_OBSERVER(Observer, observer_list_, GoogleSignedOut(username));
+}
+
+// static
+KeyedService* FakeSigninManager::Build(content::BrowserContext* context) {
+ return new FakeSigninManager(Profile::FromBrowserContext(context));
+}
+
+} // namespace gcm
diff --git a/chrome/browser/services/gcm/fake_signin_manager.h b/chrome/browser/services/gcm/fake_signin_manager.h
new file mode 100644
index 0000000..fc39509
--- /dev/null
+++ b/chrome/browser/services/gcm/fake_signin_manager.h
@@ -0,0 +1,54 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SERVICES_GCM_FAKE_SIGNIN_MANAGER_H_
+#define CHROME_BROWSER_SERVICES_GCM_FAKE_SIGNIN_MANAGER_H_
+
+#include <string>
+
+#include "base/macros.h"
+
+#if defined(OS_CHROMEOS)
+#include "components/signin/core/browser/signin_manager_base.h"
+#else
+#include "base/compiler_specific.h"
+#include "components/signin/core/browser/signin_manager.h"
+#endif
+
+class KeyedService;
+class Profile;
+
+namespace content {
+class BrowserContext;
+}
+
+namespace gcm {
+
+#if defined(OS_CHROMEOS)
+class FakeSigninManager : public SigninManagerBase {
+#else
+class FakeSigninManager : public SigninManager {
+#endif
+ public:
+ explicit FakeSigninManager(Profile* profile);
+ virtual ~FakeSigninManager();
+
+ void SignIn(const std::string& username);
+#if defined(OS_CHROMEOS)
+ void SignOut();
+#else
+ virtual void SignOut() OVERRIDE;
+#endif
+
+ static KeyedService* Build(content::BrowserContext* context);
+
+ private:
+ Profile* profile_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeSigninManager);
+};
+
+} // namespace gcm
+
+#endif // CHROME_BROWSER_SERVICES_GCM_FAKE_SIGNIN_MANAGER_H_
diff --git a/chrome/browser/services/gcm/gcm_client_mock.cc b/chrome/browser/services/gcm/gcm_client_mock.cc
index d924238..b894efa 100644
--- a/chrome/browser/services/gcm/gcm_client_mock.cc
+++ b/chrome/browser/services/gcm/gcm_client_mock.cc
@@ -143,7 +143,7 @@ void GCMClientMock::DeleteMessages(const std::string& app_id) {
// static
std::string GCMClientMock::GetRegistrationIdFromSenderIds(
const std::vector<std::string>& sender_ids) {
- // GCMProfileService normalizes the sender IDs by making them sorted.
+ // GCMService normalizes 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());
diff --git a/chrome/browser/services/gcm/gcm_profile_service.cc b/chrome/browser/services/gcm/gcm_profile_service.cc
index f93b427..0592b0c 100644
--- a/chrome/browser/services/gcm/gcm_profile_service.cc
+++ b/chrome/browser/services/gcm/gcm_profile_service.cc
@@ -4,411 +4,44 @@
#include "chrome/browser/services/gcm/gcm_profile_service.h"
-#include <algorithm>
-#include <string>
-#include <vector>
-
-#include "base/base64.h"
#include "base/files/file_path.h"
#include "base/logging.h"
-#include "base/path_service.h"
#include "base/prefs/pref_service.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/threading/sequenced_worker_pool.h"
-#include "chrome/browser/chrome_notification_types.h"
+#include "base/values.h"
#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/services/gcm/gcm_app_handler.h"
-#include "chrome/browser/services/gcm/gcm_client_factory.h"
+#include "chrome/browser/signin/profile_identity_provider.h"
#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
#include "chrome/browser/signin/signin_manager_factory.h"
#include "chrome/common/chrome_constants.h"
-#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_version_info.h"
#include "chrome/common/pref_names.h"
-#include "components/signin/core/browser/profile_oauth2_token_service.h"
#include "components/signin/core/browser/signin_manager.h"
#include "components/user_prefs/pref_registry_syncable.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/notification_details.h"
-#include "content/public/browser/notification_source.h"
-#include "google_apis/gcm/protocol/android_checkin.pb.h"
+#include "google_apis/gaia/identity_provider.h"
#include "net/url_request/url_request_context_getter.h"
-namespace gcm {
-
-namespace {
-
-checkin_proto::ChromeBuildProto_Platform GetPlatform() {
-#if defined(OS_WIN)
- return checkin_proto::ChromeBuildProto_Platform_PLATFORM_WIN;
-#elif defined(OS_MACOSX)
- return checkin_proto::ChromeBuildProto_Platform_PLATFORM_MAC;
-#elif defined(OS_IOS)
- return checkin_proto::ChromeBuildProto_Platform_PLATFORM_IOS;
-#elif defined(OS_CHROMEOS)
- return checkin_proto::ChromeBuildProto_Platform_PLATFORM_CROS;
-#elif defined(OS_LINUX)
- return checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
-#else
- // For all other platforms, return as LINUX.
- return checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
+#if !defined(OS_ANDROID)
+#include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
#endif
-}
-
-std::string GetVersion() {
- chrome::VersionInfo version_info;
- return version_info.Version();
-}
-
-checkin_proto::ChromeBuildProto_Channel GetChannel() {
- chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
- switch (channel) {
- case chrome::VersionInfo::CHANNEL_UNKNOWN:
- return checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN;
- case chrome::VersionInfo::CHANNEL_CANARY:
- return checkin_proto::ChromeBuildProto_Channel_CHANNEL_CANARY;
- case chrome::VersionInfo::CHANNEL_DEV:
- return checkin_proto::ChromeBuildProto_Channel_CHANNEL_DEV;
- case chrome::VersionInfo::CHANNEL_BETA:
- return checkin_proto::ChromeBuildProto_Channel_CHANNEL_BETA;
- case chrome::VersionInfo::CHANNEL_STABLE:
- return checkin_proto::ChromeBuildProto_Channel_CHANNEL_STABLE;
- default:
- NOTREACHED();
- return checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN;
- };
-}
-
-} // namespace
-
-// Helper class to save tasks to run until we're ready to execute them.
-class GCMProfileService::DelayedTaskController {
- public:
- DelayedTaskController();
- ~DelayedTaskController();
-
- // Adds a task that will be invoked once we're ready.
- void AddTask(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_;
-};
-
-GCMProfileService::DelayedTaskController::DelayedTaskController()
- : ready_(false) {
-}
-
-GCMProfileService::DelayedTaskController::~DelayedTaskController() {
-}
-
-void GCMProfileService::DelayedTaskController::AddTask(base::Closure task) {
- delayed_tasks_.push_back(task);
-}
-
-void GCMProfileService::DelayedTaskController::SetReady() {
- ready_ = true;
- RunTasks();
-}
-
-bool GCMProfileService::DelayedTaskController::CanRunTaskWithoutDelay() const {
- return ready_;
-}
-
-void GCMProfileService::DelayedTaskController::RunTasks() {
- DCHECK(ready_);
- for (size_t i = 0; i < delayed_tasks_.size(); ++i)
- delayed_tasks_[i].Run();
- delayed_tasks_.clear();
-}
-
-class GCMProfileService::IOWorker
- : public GCMClient::Delegate,
- public base::RefCountedThreadSafe<GCMProfileService::IOWorker>{
- public:
- // Called on UI thread.
- 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;
-
- // Called on IO thread.
- void Initialize(scoped_ptr<GCMClientFactory> gcm_client_factory,
- const base::FilePath& store_path,
- const std::vector<std::string>& account_ids,
- const scoped_refptr<net::URLRequestContextGetter>&
- url_request_context_getter);
- void Reset();
- void Load(const base::WeakPtr<GCMProfileService>& 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:
- friend class base::RefCountedThreadSafe<IOWorker>;
- virtual ~IOWorker();
-
- base::WeakPtr<GCMProfileService> service_;
-
- scoped_ptr<GCMClient> gcm_client_;
-};
-
-GCMProfileService::IOWorker::IOWorker() {
- DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-}
-
-GCMProfileService::IOWorker::~IOWorker() {
-}
-
-void GCMProfileService::IOWorker::Initialize(
- scoped_ptr<GCMClientFactory> gcm_client_factory,
- const base::FilePath& store_path,
- const std::vector<std::string>& account_ids,
- const scoped_refptr<net::URLRequestContextGetter>&
- url_request_context_getter) {
- DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
-
- gcm_client_ = gcm_client_factory->BuildInstance().Pass();
-
- checkin_proto::ChromeBuildProto chrome_build_proto;
- chrome_build_proto.set_platform(GetPlatform());
- chrome_build_proto.set_chrome_version(GetVersion());
- chrome_build_proto.set_channel(GetChannel());
-
- scoped_refptr<base::SequencedWorkerPool> worker_pool(
- content::BrowserThread::GetBlockingPool());
- scoped_refptr<base::SequencedTaskRunner> blocking_task_runner(
- worker_pool->GetSequencedTaskRunnerWithShutdownBehavior(
- worker_pool->GetSequenceToken(),
- base::SequencedWorkerPool::SKIP_ON_SHUTDOWN));
-
- gcm_client_->Initialize(chrome_build_proto,
- store_path,
- account_ids,
- blocking_task_runner,
- url_request_context_getter,
- this);
-}
-
-void GCMProfileService::IOWorker::Reset() {
- DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
-
- // GCMClient instance must be destroyed from the same thread where it was
- // created.
- gcm_client_.reset();
-}
-
-void GCMProfileService::IOWorker::OnRegisterFinished(
- const std::string& app_id,
- const std::string& registration_id,
- GCMClient::Result result) {
- DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
-
- content::BrowserThread::PostTask(
- content::BrowserThread::UI,
- FROM_HERE,
- base::Bind(&GCMProfileService::RegisterFinished,
- service_,
- app_id,
- registration_id,
- result));
-}
-
-void GCMProfileService::IOWorker::OnUnregisterFinished(
- const std::string& app_id,
- GCMClient::Result result) {
- DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
-
- content::BrowserThread::PostTask(
- content::BrowserThread::UI,
- FROM_HERE,
- base::Bind(
- &GCMProfileService::UnregisterFinished, service_, app_id, result));
-}
-
-void GCMProfileService::IOWorker::OnSendFinished(
- const std::string& app_id,
- const std::string& message_id,
- GCMClient::Result result) {
- DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
-
- content::BrowserThread::PostTask(
- content::BrowserThread::UI,
- FROM_HERE,
- base::Bind(&GCMProfileService::SendFinished,
- service_,
- app_id,
- message_id,
- result));
-}
-
-void GCMProfileService::IOWorker::OnMessageReceived(
- const std::string& app_id,
- const GCMClient::IncomingMessage& message) {
- DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
-
- content::BrowserThread::PostTask(
- content::BrowserThread::UI,
- FROM_HERE,
- base::Bind(&GCMProfileService::MessageReceived,
- service_,
- app_id,
- message));
-}
-
-void GCMProfileService::IOWorker::OnMessagesDeleted(const std::string& app_id) {
- DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
-
- content::BrowserThread::PostTask(
- content::BrowserThread::UI,
- FROM_HERE,
- base::Bind(&GCMProfileService::MessagesDeleted,
- service_,
- app_id));
-}
-
-void GCMProfileService::IOWorker::OnMessageSendError(
- const std::string& app_id,
- const GCMClient::SendErrorDetails& send_error_details) {
- DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
-
- content::BrowserThread::PostTask(
- content::BrowserThread::UI,
- FROM_HERE,
- base::Bind(&GCMProfileService::MessageSendError,
- service_,
- app_id,
- send_error_details));
-}
-
-void GCMProfileService::IOWorker::OnGCMReady() {
- content::BrowserThread::PostTask(
- content::BrowserThread::UI,
- FROM_HERE,
- base::Bind(&GCMProfileService::GCMClientReady,
- service_));
-}
-
-void GCMProfileService::IOWorker::Load(
- const base::WeakPtr<GCMProfileService>& service) {
- DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
-
- service_ = service;
- gcm_client_->Load();
-}
-
-void GCMProfileService::IOWorker::Stop() {
- DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
-
- gcm_client_->Stop();
-}
-
-void GCMProfileService::IOWorker::CheckOut() {
- DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
-
- gcm_client_->CheckOut();
-
- // Note that we still need to keep GCMClient instance alive since the profile
- // might be signed in again.
-}
-
-void GCMProfileService::IOWorker::Register(
- const std::string& app_id,
- const std::vector<std::string>& sender_ids) {
- DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
-
- gcm_client_->Register(app_id, sender_ids);
-}
-
-void GCMProfileService::IOWorker::Unregister(const std::string& app_id) {
- DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
-
- gcm_client_->Unregister(app_id);
-}
-
-void GCMProfileService::IOWorker::Send(
- const std::string& app_id,
- const std::string& receiver_id,
- const GCMClient::OutgoingMessage& message) {
- DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
-
- gcm_client_->Send(app_id, receiver_id, message);
-}
-
-void GCMProfileService::IOWorker::GetGCMStatistics(bool clear_logs) {
- DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
- gcm::GCMClient::GCMStatistics stats;
-
- if (gcm_client_.get()) {
- if (clear_logs)
- gcm_client_->ClearActivityLogs();
- stats = gcm_client_->GetStatistics();
- }
-
- content::BrowserThread::PostTask(
- content::BrowserThread::UI,
- FROM_HERE,
- base::Bind(&GCMProfileService::GetGCMStatisticsFinished,
- service_,
- stats));
-}
+namespace gcm {
-void GCMProfileService::IOWorker::SetGCMRecording(bool recording) {
- DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
- gcm::GCMClient::GCMStatistics stats;
+// static
+GCMProfileService::GCMEnabledState GCMProfileService::GetGCMEnabledState(
+ Profile* profile) {
+ const base::Value* gcm_enabled_value =
+ profile->GetPrefs()->GetUserPrefValue(prefs::kGCMChannelEnabled);
+ if (!gcm_enabled_value)
+ return ENABLED_FOR_APPS;
- if (gcm_client_.get()) {
- gcm_client_->SetRecording(recording);
- stats = gcm_client_->GetStatistics();
- stats.gcm_client_created = true;
- }
+ bool gcm_enabled = false;
+ if (!gcm_enabled_value->GetAsBoolean(&gcm_enabled))
+ return ENABLED_FOR_APPS;
- content::BrowserThread::PostTask(
- content::BrowserThread::UI,
- FROM_HERE,
- base::Bind(&GCMProfileService::GetGCMStatisticsFinished,
- service_,
- stats));
+ return gcm_enabled ? ALWAYS_ENABLED : ALWAYS_DISABLED;
}
+// static
std::string GCMProfileService::GetGCMEnabledStateString(GCMEnabledState state) {
switch (state) {
case GCMProfileService::ALWAYS_ENABLED:
@@ -424,21 +57,6 @@ std::string GCMProfileService::GetGCMEnabledStateString(GCMEnabledState state) {
}
// static
-GCMProfileService::GCMEnabledState GCMProfileService::GetGCMEnabledState(
- Profile* profile) {
- const base::Value* gcm_enabled_value =
- profile->GetPrefs()->GetUserPrefValue(prefs::kGCMChannelEnabled);
- if (!gcm_enabled_value)
- return ENABLED_FOR_APPS;
-
- bool gcm_enabled = false;
- if (!gcm_enabled_value->GetAsBoolean(&gcm_enabled))
- return ENABLED_FOR_APPS;
-
- return gcm_enabled ? ALWAYS_ENABLED : ALWAYS_DISABLED;
-}
-
-// static
void GCMProfileService::RegisterProfilePrefs(
user_prefs::PrefRegistrySyncable* registry) {
// GCM support is only enabled by default for Canary/Dev/Custom builds.
@@ -456,492 +74,42 @@ void GCMProfileService::RegisterProfilePrefs(
}
GCMProfileService::GCMProfileService(Profile* profile)
- : profile_(profile),
- gcm_client_ready_(false),
- weak_ptr_factory_(this) {
+ : GCMService(scoped_ptr<IdentityProvider>(new ProfileIdentityProvider(
+ SigninManagerFactory::GetForProfile(profile),
+ ProfileOAuth2TokenServiceFactory::GetForProfile(profile),
+#if defined(OS_ANDROID)
+ NULL))),
+#else
+ LoginUIServiceFactory::GetForProfile(profile)))),
+#endif
+ profile_(profile) {
DCHECK(!profile->IsOffTheRecord());
}
GCMProfileService::~GCMProfileService() {
}
-void GCMProfileService::Initialize(
- scoped_ptr<GCMClientFactory> gcm_client_factory) {
- registrar_.Add(this,
- chrome::NOTIFICATION_PROFILE_DESTROYED,
- content::Source<Profile>(profile_));
-
- SigninManagerFactory::GetForProfile(profile_)->AddObserver(this);
-
- // Get the list of available accounts.
- std::vector<std::string> account_ids;
-#if !defined(OS_ANDROID)
- account_ids =
- ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)->GetAccounts();
-#endif
-
- // Create and initialize the GCMClient. Note that this does not initiate the
- // GCM check-in.
- io_worker_ = new IOWorker();
- scoped_refptr<net::URLRequestContextGetter> url_request_context_getter =
- profile_->GetRequestContext();
- content::BrowserThread::PostTask(
- content::BrowserThread::IO,
- FROM_HERE,
- base::Bind(&GCMProfileService::IOWorker::Initialize,
- io_worker_,
- base::Passed(&gcm_client_factory),
- profile_->GetPath().Append(chrome::kGCMStoreDirname),
- account_ids,
- url_request_context_getter));
-
- // Load from the GCM store and initiate the GCM check-in if the rollout signal
- // indicates yes.
- if (GetGCMEnabledState(profile_) == ALWAYS_ENABLED)
- EnsureLoaded();
-}
-
-void GCMProfileService::Start() {
- DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-
- EnsureLoaded();
-}
-
-void GCMProfileService::Stop() {
- DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-
- // No need to stop GCM service if not started yet.
- if (username_.empty())
- return;
-
- RemoveCachedData();
-
- content::BrowserThread::PostTask(
- content::BrowserThread::IO,
- FROM_HERE,
- base::Bind(&GCMProfileService::IOWorker::Stop, io_worker_));
-}
-
void GCMProfileService::Shutdown() {
- for (GCMAppHandlerMap::const_iterator iter = app_handlers_.begin();
- iter != app_handlers_.end(); ++iter) {
- iter->second->ShutdownHandler();
- }
- app_handlers_.clear();
-
- SigninManagerFactory::GetForProfile(profile_)->RemoveObserver(this);
-}
-
-void GCMProfileService::AddAppHandler(const std::string& app_id,
- GCMAppHandler* handler) {
- DCHECK(!app_id.empty());
- DCHECK(handler);
- DCHECK(app_handlers_.find(app_id) == app_handlers_.end());
-
- app_handlers_[app_id] = handler;
-}
-
-void GCMProfileService::RemoveAppHandler(const std::string& app_id) {
- DCHECK(!app_id.empty());
-
- app_handlers_.erase(app_id);
-}
-
-void GCMProfileService::Register(const std::string& app_id,
- const std::vector<std::string>& sender_ids,
- RegisterCallback callback) {
- DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
- DCHECK(!app_id.empty() && !sender_ids.empty() && !callback.is_null());
-
- GCMClient::Result result = EnsureAppReady(app_id);
- 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(&GCMProfileService::DoRegister,
- weak_ptr_factory_.GetWeakPtr(),
- app_id,
- sender_ids));
- return;
- }
-
- DoRegister(app_id, sender_ids);
-}
-
-void GCMProfileService::DoRegister(const std::string& app_id,
- const std::vector<std::string>& sender_ids) {
- 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());
-
- content::BrowserThread::PostTask(
- content::BrowserThread::IO,
- FROM_HERE,
- base::Bind(&GCMProfileService::IOWorker::Register,
- io_worker_,
- app_id,
- normalized_sender_ids));
-}
-
-void GCMProfileService::Unregister(const std::string& app_id,
- UnregisterCallback callback) {
- DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
- DCHECK(!app_id.empty() && !callback.is_null());
-
- GCMClient::Result result = EnsureAppReady(app_id);
- 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(&GCMProfileService::DoUnregister,
- weak_ptr_factory_.GetWeakPtr(),
- app_id));
- return;
- }
-
- DoUnregister(app_id);
-}
-
-void GCMProfileService::DoUnregister(const std::string& app_id) {
- DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-
- // 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.
- content::BrowserThread::PostTask(
- content::BrowserThread::IO,
- FROM_HERE,
- base::Bind(&GCMProfileService::IOWorker::Unregister,
- io_worker_,
- app_id));
-}
-
-void GCMProfileService::Send(const std::string& app_id,
- const std::string& receiver_id,
- const GCMClient::OutgoingMessage& message,
- SendCallback callback) {
- DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
- DCHECK(!app_id.empty() && !receiver_id.empty() && !callback.is_null());
-
- GCMClient::Result result = EnsureAppReady(app_id);
- 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(&GCMProfileService::DoSend,
- weak_ptr_factory_.GetWeakPtr(),
- app_id,
- receiver_id,
- message));
- return;
- }
-
- DoSend(app_id, receiver_id, message);
-}
-
-void GCMProfileService::DoSend(const std::string& app_id,
- const std::string& receiver_id,
- const GCMClient::OutgoingMessage& message) {
- content::BrowserThread::PostTask(
- content::BrowserThread::IO,
- FROM_HERE,
- base::Bind(&GCMProfileService::IOWorker::Send,
- io_worker_,
- app_id,
- receiver_id,
- message));
-}
-
-GCMClient* GCMProfileService::GetGCMClientForTesting() const {
- return io_worker_ ? io_worker_->gcm_client_for_testing() : NULL;
+ ShutdownService();
}
std::string GCMProfileService::SignedInUserName() const {
- return username_;
-}
-
-bool GCMProfileService::IsGCMClientReady() const {
- return gcm_client_ready_;
+ if (IsStarted())
+ return identity_provider_->GetActiveUsername();
+ return std::string();
}
-void GCMProfileService::GetGCMStatistics(
- GetGCMStatisticsCallback callback, bool clear_logs) {
- DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
- DCHECK(!callback.is_null());
-
- request_gcm_statistics_callback_ = callback;
- content::BrowserThread::PostTask(
- content::BrowserThread::IO,
- FROM_HERE,
- base::Bind(&GCMProfileService::IOWorker::GetGCMStatistics,
- io_worker_,
- clear_logs));
-}
-
-void GCMProfileService::SetGCMRecording(
- GetGCMStatisticsCallback callback, bool recording) {
- DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-
- request_gcm_statistics_callback_ = callback;
- content::BrowserThread::PostTask(
- content::BrowserThread::IO,
- FROM_HERE,
- base::Bind(&GCMProfileService::IOWorker::SetGCMRecording,
- io_worker_,
- recording));
-}
-
-void GCMProfileService::Observe(int type,
- const content::NotificationSource& source,
- const content::NotificationDetails& details) {
- DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-
- switch (type) {
- case chrome::NOTIFICATION_PROFILE_DESTROYED:
- ResetGCMClient();
- break;
- default:
- NOTREACHED();
- }
-}
-
-void GCMProfileService::GoogleSigninSucceeded(const std::string& username,
- const std::string& password) {
- if (GetGCMEnabledState(profile_) == ALWAYS_ENABLED)
- EnsureLoaded();
-}
-
-void GCMProfileService::GoogleSignedOut(const std::string& username) {
- CheckOut();
-}
-
-void GCMProfileService::EnsureLoaded() {
- SigninManagerBase* manager = SigninManagerFactory::GetForProfile(profile_);
- if (!manager)
- return;
- std::string username = manager->GetAuthenticatedUsername();
- if (username.empty())
- return;
-
- // CheckIn could be called more than once when:
- // 1) The password changes.
- // 2) Register/send function calls it to ensure CheckIn is done.
- if (username_ == username)
- return;
- username_ = username;
-
- DCHECK(!delayed_task_controller_);
- delayed_task_controller_.reset(new DelayedTaskController);
-
- // This will load the data from the gcm store and trigger the check-in if
- // the persisted check-in info is not found.
- // Note that we need to pass weak pointer again since the existing weak
- // pointer in IOWorker might have been invalidated when check-out occurs.
- content::BrowserThread::PostTask(
- content::BrowserThread::IO,
- FROM_HERE,
- base::Bind(&GCMProfileService::IOWorker::Load,
- io_worker_,
- weak_ptr_factory_.GetWeakPtr()));
-}
-
-void GCMProfileService::RemoveCachedData() {
- // Remove all the queued tasks since they no longer make sense after
- // GCM service is stopped.
- weak_ptr_factory_.InvalidateWeakPtrs();
-
- username_.clear();
- gcm_client_ready_ = false;
- delayed_task_controller_.reset();
- register_callbacks_.clear();
- send_callbacks_.clear();
-}
-
-void GCMProfileService::CheckOut() {
- DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-
- // 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();
-
- content::BrowserThread::PostTask(
- content::BrowserThread::IO,
- FROM_HERE,
- base::Bind(&GCMProfileService::IOWorker::CheckOut, io_worker_));
-}
-
-void GCMProfileService::ResetGCMClient() {
- content::BrowserThread::PostTask(
- content::BrowserThread::IO,
- FROM_HERE,
- base::Bind(&GCMProfileService::IOWorker::Reset, io_worker_));
-}
-
-GCMClient::Result GCMProfileService::EnsureAppReady(const std::string& app_id) {
- // Ensure that check-in has been done.
- EnsureLoaded();
-
- // If the profile was not signed in, bail out.
- if (username_.empty())
- return GCMClient::NOT_SIGNED_IN;
-
- return GCMClient::SUCCESS;
-}
-
-bool GCMProfileService::IsAsyncOperationPending(
- const std::string& app_id) const {
- return register_callbacks_.find(app_id) != register_callbacks_.end() ||
- unregister_callbacks_.find(app_id) != unregister_callbacks_.end();
-}
-
-void GCMProfileService::RegisterFinished(const std::string& app_id,
- const std::string& registration_id,
- GCMClient::Result result) {
- DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-
- 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 GCMProfileService::UnregisterFinished(const std::string& app_id,
- GCMClient::Result result) {
- DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-
- 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 GCMProfileService::SendFinished(const std::string& app_id,
- const std::string& message_id,
- GCMClient::Result result) {
- DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-
- 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 GCMProfileService::MessageReceived(const std::string& app_id,
- GCMClient::IncomingMessage message) {
- DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-
- // Drop the event if signed out.
- if (username_.empty())
- return;
-
- GetAppHandler(app_id)->OnMessage(app_id, message);
-}
-
-void GCMProfileService::MessagesDeleted(const std::string& app_id) {
- DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-
- // Drop the event if signed out.
- if (username_.empty())
- return;
-
- GetAppHandler(app_id)->OnMessagesDeleted(app_id);
-}
-
-void GCMProfileService::MessageSendError(
- const std::string& app_id,
- const GCMClient::SendErrorDetails& send_error_details) {
- DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-
- // Drop the event if signed out.
- if (username_.empty())
- return;
-
- GetAppHandler(app_id)->OnSendError(app_id, send_error_details);
-}
-
-void GCMProfileService::GCMClientReady() {
- DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-
- if (gcm_client_ready_)
- return;
- gcm_client_ready_ = true;
-
- delayed_task_controller_->SetReady();
+bool GCMProfileService::ShouldStartAutomatically() const {
+ return GetGCMEnabledState(profile_) == ALWAYS_ENABLED;
}
-GCMAppHandler* GCMProfileService::GetAppHandler(const std::string& app_id) {
- std::map<std::string, GCMAppHandler*>::const_iterator iter =
- app_handlers_.find(app_id);
- return iter == app_handlers_.end() ? &default_app_handler_ : iter->second;
+base::FilePath GCMProfileService::GetStorePath() const {
+ return profile_->GetPath().Append(chrome::kGCMStoreDirname);
}
-void GCMProfileService::GetGCMStatisticsFinished(
- GCMClient::GCMStatistics stats) {
- DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
- request_gcm_statistics_callback_.Run(stats);
+scoped_refptr<net::URLRequestContextGetter>
+GCMProfileService::GetURLRequestContextGetter() const {
+ return profile_->GetRequestContext();
}
} // namespace gcm
diff --git a/chrome/browser/services/gcm/gcm_profile_service.h b/chrome/browser/services/gcm/gcm_profile_service.h
index b93bfa2..56029b5 100644
--- a/chrome/browser/services/gcm/gcm_profile_service.h
+++ b/chrome/browser/services/gcm/gcm_profile_service.h
@@ -5,55 +5,25 @@
#ifndef CHROME_BROWSER_SERVICES_GCM_GCM_PROFILE_SERVICE_H_
#define CHROME_BROWSER_SERVICES_GCM_GCM_PROFILE_SERVICE_H_
-#include <map>
#include <string>
-#include "base/basictypes.h"
-#include "base/callback.h"
#include "base/compiler_specific.h"
-#include "base/gtest_prod_util.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
-#include "chrome/browser/services/gcm/default_gcm_app_handler.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "chrome/browser/services/gcm/gcm_service.h"
#include "components/keyed_service/core/keyed_service.h"
-#include "components/signin/core/browser/signin_manager_base.h"
-#include "content/public/browser/notification_observer.h"
-#include "content/public/browser/notification_registrar.h"
-#include "google_apis/gcm/gcm_client.h"
class Profile;
-namespace base {
-class Value;
-}
-
-namespace extensions {
-class ExtensionGCMAppHandlerTest;
-}
-
namespace user_prefs {
class PrefRegistrySyncable;
}
namespace gcm {
-class GCMAppHandler;
-class GCMClientFactory;
-class GCMProfileServiceTestConsumer;
-
-// Acts as a bridge between GCM API and GCMClient layer. It is profile based.
-class GCMProfileService : public KeyedService,
- public content::NotificationObserver,
- public SigninManagerBase::Observer {
+// A specialization of GCMService that is tied to a Profile.
+class GCMProfileService : public GCMService, public KeyedService {
public:
- typedef base::Callback<void(const std::string& registration_id,
- GCMClient::Result result)> RegisterCallback;
- typedef base::Callback<void(const std::string& message_id,
- GCMClient::Result result)> SendCallback;
- typedef base::Callback<void(GCMClient::Result result)> UnregisterCallback;
- typedef base::Callback<void(const GCMClient::GCMStatistics& stats)>
- GetGCMStatisticsCallback;
-
// Any change made to this enum should have corresponding change in the
// GetGCMEnabledStateString(...) function.
enum GCMEnabledState {
@@ -78,172 +48,23 @@ class GCMProfileService : public KeyedService,
explicit GCMProfileService(Profile* profile);
virtual ~GCMProfileService();
- void Initialize(scoped_ptr<GCMClientFactory> gcm_client_factory);
-
- void Start();
-
- void Stop();
-
- // KeyedService implementation.
+ // KeyedService:
virtual void Shutdown() OVERRIDE;
- // Adds a handler for a given app.
- virtual void AddAppHandler(const std::string& app_id, GCMAppHandler* handler);
-
- // Remove the handler for a given app.
- virtual void RemoveAppHandler(const std::string& app_id);
-
- // Registers |sender_id| for an app. A registration ID will be returned by
- // the GCM server.
- // |app_id|: application ID.
- // |sender_ids|: list of IDs of the servers that are allowed to send the
- // messages to the application. These IDs are assigned by the
- // Google API Console.
- // |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,
- RegisterCallback callback);
-
- // 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,
- UnregisterCallback callback);
-
- // Sends a message to a given receiver.
- // |app_id|: application ID.
- // |receiver_id|: registration ID of the receiver party.
- // |message|: message to be sent.
- // |callback|: to be called once the asynchronous operation is done.
- virtual void Send(const std::string& app_id,
- const std::string& receiver_id,
- const GCMClient::OutgoingMessage& message,
- SendCallback callback);
-
- // For testing purpose.
- GCMClient* GetGCMClientForTesting() const;
-
// Returns the user name if the profile is signed in.
std::string SignedInUserName() const;
- // Returns true if the gcm client is ready.
- bool IsGCMClientReady() const;
-
- // 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(GetGCMStatisticsCallback callback,
- bool clear_logs);
-
- // Enables/disables GCM activity recording, and then returns the stats.
- void SetGCMRecording(GetGCMStatisticsCallback callback, bool recording);
+ protected:
+ // Overridden from GCMService:
+ virtual bool ShouldStartAutomatically() const OVERRIDE;
+ virtual base::FilePath GetStorePath() const OVERRIDE;
+ virtual scoped_refptr<net::URLRequestContextGetter>
+ GetURLRequestContextGetter() const OVERRIDE;
private:
- friend class GCMProfileServiceTestConsumer;
- friend class extensions::ExtensionGCMAppHandlerTest;
-
- class DelayedTaskController;
- class IOWorker;
-
- typedef std::map<std::string, GCMAppHandler*> GCMAppHandlerMap;
-
- // Overridden from content::NotificationObserver:
- virtual void Observe(int type,
- const content::NotificationSource& source,
- const content::NotificationDetails& details) OVERRIDE;
-
- // Overridden from SigninManagerBase::Observer:
- virtual void GoogleSigninSucceeded(const std::string& username,
- const std::string& password) OVERRIDE;
- virtual void GoogleSignedOut(const std::string& username) OVERRIDE;
-
- // Ensures that the GCMClient is loaded and the GCM check-in is done when
- // the profile was signed in.
- void EnsureLoaded();
-
- // Remove cached data when GCM service is stopped.
- void RemoveCachedData();
-
- // Checks out of GCM when the profile has been signed out. This will erase
- // all the cached and persisted data.
- void CheckOut();
-
- // Resets the GCMClient instance. This is called when the profile is being
- // destroyed.
- void ResetGCMClient();
-
- // Ensures that the app is ready for GCM functions and events.
- GCMClient::Result EnsureAppReady(const std::string& app_id);
-
- // 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);
-
// The profile which owns this object.
Profile* profile_;
- // Flag to indicate if GCMClient is ready.
- bool gcm_client_ready_;
-
- // The username of the signed-in profile.
- std::string username_;
-
- content::NotificationRegistrar registrar_;
-
- scoped_ptr<DelayedTaskController> delayed_task_controller_;
-
- // For all the work occured in IO thread.
- scoped_refptr<IOWorker> io_worker_;
-
- // App handler map (from app_id to handler pointer).
- // The handler is not owned.
- GCMAppHandlerMap app_handlers_;
-
- // 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<GCMProfileService> weak_ptr_factory_;
-
DISALLOW_COPY_AND_ASSIGN(GCMProfileService);
};
diff --git a/chrome/browser/services/gcm/gcm_profile_service_factory.cc b/chrome/browser/services/gcm/gcm_profile_service_factory.cc
index 2a5b4c5..437fa5f 100644
--- a/chrome/browser/services/gcm/gcm_profile_service_factory.cc
+++ b/chrome/browser/services/gcm/gcm_profile_service_factory.cc
@@ -4,13 +4,19 @@
#include "chrome/browser/services/gcm/gcm_profile_service_factory.h"
+#include "base/memory/scoped_ptr.h"
#include "chrome/browser/profiles/incognito_helpers.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/services/gcm/gcm_client_factory.h"
#include "chrome/browser/services/gcm/gcm_profile_service.h"
+#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
#include "chrome/browser/signin/signin_manager_factory.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#if !defined(OS_ANDROID)
+#include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
+#endif
+
namespace gcm {
// static
@@ -33,6 +39,10 @@ GCMProfileServiceFactory::GCMProfileServiceFactory()
"GCMProfileService",
BrowserContextDependencyManager::GetInstance()) {
DependsOn(SigninManagerFactory::GetInstance());
+ DependsOn(ProfileOAuth2TokenServiceFactory::GetInstance());
+#if !defined(OS_ANDROID)
+ DependsOn(LoginUIServiceFactory::GetInstance());
+#endif
}
GCMProfileServiceFactory::~GCMProfileServiceFactory() {
diff --git a/chrome/browser/services/gcm/gcm_profile_service_factory.h b/chrome/browser/services/gcm/gcm_profile_service_factory.h
index be27b7d..2e5763e 100644
--- a/chrome/browser/services/gcm/gcm_profile_service_factory.h
+++ b/chrome/browser/services/gcm/gcm_profile_service_factory.h
@@ -16,8 +16,7 @@ namespace gcm {
class GCMProfileService;
// Singleton that owns all GCMProfileService and associates them with
-// Profiles. Listens for the Profile's destruction notification and cleans up
-// the associated GCMProfileService.
+// Profiles.
class GCMProfileServiceFactory : public BrowserContextKeyedServiceFactory {
public:
static GCMProfileService* GetForProfile(Profile* profile);
diff --git a/chrome/browser/services/gcm/gcm_profile_service_test_helper.cc b/chrome/browser/services/gcm/gcm_profile_service_test_helper.cc
deleted file mode 100644
index 2379482..0000000
--- a/chrome/browser/services/gcm/gcm_profile_service_test_helper.cc
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/services/gcm/gcm_profile_service_test_helper.h"
-
-#include "base/bind.h"
-#include "base/prefs/pref_service.h"
-#include "base/run_loop.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/signin/chrome_signin_client_factory.h"
-#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
-#include "chrome/common/pref_names.h"
-#include "content/public/browser/browser_thread.h"
-
-namespace gcm {
-
-Waiter::Waiter() {
-}
-
-Waiter::~Waiter() {
-}
-
-void Waiter::WaitUntilCompleted() {
- run_loop_.reset(new base::RunLoop);
- run_loop_->Run();
-}
-
-void Waiter::SignalCompleted() {
- if (run_loop_ && run_loop_->running())
- run_loop_->Quit();
-}
-
-void Waiter::PumpUILoop() {
- base::MessageLoop::current()->RunUntilIdle();
-}
-
-void Waiter::PumpIOLoop() {
- content::BrowserThread::PostTask(
- content::BrowserThread::IO,
- FROM_HERE,
- base::Bind(&Waiter::OnIOLoopPump, base::Unretained(this)));
-
- WaitUntilCompleted();
-}
-
-void Waiter::PumpIOLoopCompleted() {
- DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-
- SignalCompleted();
-}
-
-void Waiter::OnIOLoopPump() {
- DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
-
- content::BrowserThread::PostTask(
- content::BrowserThread::IO,
- FROM_HERE,
- base::Bind(&Waiter::OnIOLoopPumpCompleted, base::Unretained(this)));
-}
-
-void Waiter::OnIOLoopPumpCompleted() {
- DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
-
- content::BrowserThread::PostTask(
- content::BrowserThread::UI,
- FROM_HERE,
- base::Bind(&Waiter::PumpIOLoopCompleted,
- base::Unretained(this)));
-}
-
-// static
-KeyedService* FakeSigninManager::Build(content::BrowserContext* context) {
- return new FakeSigninManager(static_cast<Profile*>(context));
-}
-
-FakeSigninManager::FakeSigninManager(Profile* profile)
-#if defined(OS_CHROMEOS)
- : SigninManagerBase(
- ChromeSigninClientFactory::GetInstance()->GetForProfile(profile)),
-#else
- : SigninManager(
- ChromeSigninClientFactory::GetInstance()->GetForProfile(profile),
- ProfileOAuth2TokenServiceFactory::GetForProfile(profile)),
-#endif
- profile_(profile) {
- Initialize(NULL);
-}
-
-FakeSigninManager::~FakeSigninManager() {
-}
-
-void FakeSigninManager::SignIn(const std::string& username) {
- SetAuthenticatedUsername(username);
- FOR_EACH_OBSERVER(Observer,
- observer_list_,
- GoogleSigninSucceeded(username, std::string()));
-}
-
-void FakeSigninManager::SignOut() {
- std::string username = GetAuthenticatedUsername();
- clear_authenticated_username();
- profile_->GetPrefs()->ClearPref(prefs::kGoogleServicesUsername);
- FOR_EACH_OBSERVER(Observer, observer_list_, GoogleSignedOut(username));
-}
-
-FakeGCMClientFactory::FakeGCMClientFactory(
- GCMClientMock::LoadingDelay gcm_client_loading_delay)
- : gcm_client_loading_delay_(gcm_client_loading_delay) {
-}
-
-FakeGCMClientFactory::~FakeGCMClientFactory() {
-}
-
-scoped_ptr<GCMClient> FakeGCMClientFactory::BuildInstance() {
- return scoped_ptr<GCMClient>(new GCMClientMock(gcm_client_loading_delay_));
-}
-
-} // namespace gcm
diff --git a/chrome/browser/services/gcm/gcm_profile_service_test_helper.h b/chrome/browser/services/gcm/gcm_profile_service_test_helper.h
deleted file mode 100644
index 66eea37..0000000
--- a/chrome/browser/services/gcm/gcm_profile_service_test_helper.h
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_SERVICES_GCM_GCM_PROFILE_SERVICE_TEST_HELPER_H_
-#define CHROME_BROWSER_SERVICES_GCM_GCM_PROFILE_SERVICE_TEST_HELPER_H_
-
-#include <string>
-#include "base/macros.h"
-#include "base/memory/scoped_ptr.h"
-#include "chrome/browser/services/gcm/gcm_client_factory.h"
-#include "chrome/browser/services/gcm/gcm_client_mock.h"
-#include "components/signin/core/browser/signin_manager.h"
-
-class KeyedService;
-class Profile;
-namespace base {
-class RunLoop;
-}
-namespace content {
-class BrowserContext;
-}
-
-namespace gcm {
-
-// Helper class for asynchronous waiting.
-class Waiter {
- public:
- Waiter();
- ~Waiter();
-
- // Waits until the asynchrnous operation finishes.
- void WaitUntilCompleted();
-
- // Signals that the asynchronous operation finishes.
- void SignalCompleted();
-
- // Runs until UI loop becomes idle.
- void PumpUILoop();
-
- // Runs until IO loop becomes idle.
- void PumpIOLoop();
-
- private:
- void PumpIOLoopCompleted();
- void OnIOLoopPump();
- void OnIOLoopPumpCompleted();
-
- scoped_ptr<base::RunLoop> run_loop_;
-
- DISALLOW_COPY_AND_ASSIGN(Waiter);
-};
-
-#if defined(OS_CHROMEOS)
-class FakeSigninManager : public SigninManagerBase {
-#else
-class FakeSigninManager : public SigninManager {
-#endif
- public:
- static KeyedService* Build(content::BrowserContext* context);
-
- explicit FakeSigninManager(Profile* profile);
- virtual ~FakeSigninManager();
-
- void SignIn(const std::string& username);
-#if defined(OS_CHROMEOS)
- void SignOut();
-#else
- virtual void SignOut() OVERRIDE;
-#endif
-
- private:
- Profile* profile_;
-
- DISALLOW_COPY_AND_ASSIGN(FakeSigninManager);
-};
-
-class FakeGCMClientFactory : public GCMClientFactory {
- public:
- explicit FakeGCMClientFactory(
- GCMClientMock::LoadingDelay gcm_client_loading_delay);
- virtual ~FakeGCMClientFactory();
-
- virtual scoped_ptr<GCMClient> BuildInstance() OVERRIDE;
-
- private:
- GCMClientMock::LoadingDelay gcm_client_loading_delay_;
-
- DISALLOW_COPY_AND_ASSIGN(FakeGCMClientFactory);
-};
-
-} // namespace gcm
-
-#endif // CHROME_BROWSER_SERVICES_GCM_GCM_PROFILE_SERVICE_TEST_HELPER_H_
diff --git a/chrome/browser/services/gcm/gcm_profile_service_unittest.cc b/chrome/browser/services/gcm/gcm_profile_service_unittest.cc
index 9be82fe..6fef486 100644
--- a/chrome/browser/services/gcm/gcm_profile_service_unittest.cc
+++ b/chrome/browser/services/gcm/gcm_profile_service_unittest.cc
@@ -1,1243 +1,190 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "chrome/browser/services/gcm/gcm_profile_service.h"
+
+#include <vector>
+
#include "base/bind.h"
-#include "base/prefs/pref_service.h"
-#include "base/strings/string_util.h"
-#include "chrome/browser/chrome_notification_types.h"
-#include "chrome/browser/services/gcm/gcm_app_handler.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/run_loop.h"
+#include "chrome/browser/services/gcm/fake_gcm_client_factory.h"
+#include "chrome/browser/services/gcm/fake_signin_manager.h"
#include "chrome/browser/services/gcm/gcm_client_factory.h"
#include "chrome/browser/services/gcm/gcm_client_mock.h"
-#include "chrome/browser/services/gcm/gcm_profile_service.h"
#include "chrome/browser/services/gcm/gcm_profile_service_factory.h"
-#include "chrome/browser/services/gcm/gcm_profile_service_test_helper.h"
#include "chrome/browser/signin/signin_manager_factory.h"
-#include "chrome/common/pref_names.h"
#include "chrome/test/base/testing_profile.h"
-#include "content/public/browser/browser_thread.h"
+#include "components/user_prefs/pref_registry_syncable.h"
+#include "content/public/browser/browser_context.h"
#include "content/public/test/test_browser_thread_bundle.h"
+#include "google_apis/gcm/gcm_client.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace gcm {
namespace {
-const char kTestingUsername[] = "user1@example.com";
-const char kTestingUsername2[] = "user2@example.com";
-const char kTestingUsername3[] = "user3@example.com";
-const char kTestingAppId[] = "TestApp1";
-const char kTestingAppId2[] = "TestApp2";
-const char kUserId[] = "user1";
-const char kUserId2[] = "user2";
+const char kTestAccountID[] = "user@example.com";
+const char kTestAppID[] = "TestApp";
+const char kUserID[] = "user";
-std::vector<std::string> ToSenderList(const std::string& sender_ids) {
- std::vector<std::string> senders;
- Tokenize(sender_ids, ",", &senders);
- return senders;
+KeyedService* BuildGCMProfileService(content::BrowserContext* context) {
+ return new GCMProfileService(Profile::FromBrowserContext(context));
}
} // namespace
-class FakeGCMAppHandler : public GCMAppHandler {
- public:
- enum Event {
- NO_EVENT,
- MESSAGE_EVENT,
- MESSAGES_DELETED_EVENT,
- SEND_ERROR_EVENT
- };
-
- explicit FakeGCMAppHandler(Waiter* waiter)
- : waiter_(waiter),
- received_event_(NO_EVENT) {
- }
-
- virtual ~FakeGCMAppHandler() {
- }
-
- virtual void ShutdownHandler() OVERRIDE {
- }
-
- virtual void OnMessage(const std::string& app_id,
- const GCMClient::IncomingMessage& message) OVERRIDE {
- clear_results();
- received_event_ = MESSAGE_EVENT;
- app_id_ = app_id;
- message_ = message;
- waiter_->SignalCompleted();
- }
-
- virtual void OnMessagesDeleted(const std::string& app_id) OVERRIDE {
- clear_results();
- received_event_ = MESSAGES_DELETED_EVENT;
- app_id_ = app_id;
- waiter_->SignalCompleted();
- }
-
- virtual void OnSendError(
- const std::string& app_id,
- const GCMClient::SendErrorDetails& send_error_details) OVERRIDE {
- clear_results();
- received_event_ = SEND_ERROR_EVENT;
- app_id_ = app_id;
- send_error_details_ = send_error_details;
- waiter_->SignalCompleted();
- }
-
- Event received_event() const { return received_event_; }
- const std::string& app_id() const { return app_id_; }
- const GCMClient::IncomingMessage& message() const { return message_; }
- const GCMClient::SendErrorDetails& send_error_details() const {
- return send_error_details_;
- }
- const std::string& send_error_message_id() const {
- return send_error_details_.message_id;
- }
- GCMClient::Result send_error_result() const {
- return send_error_details_.result;
- }
- const GCMClient::MessageData& send_error_data() const {
- return send_error_details_.additional_data;
- }
-
- void clear_results() {
- received_event_ = NO_EVENT;
- app_id_.clear();
- message_.data.clear();
- send_error_details_ = GCMClient::SendErrorDetails();
- }
-
- private:
- Waiter* waiter_;
- Event received_event_;
- std::string app_id_;
- GCMClient::IncomingMessage message_;
- GCMClient::SendErrorDetails send_error_details_;
-};
-
-class GCMProfileServiceTestConsumer {
- public:
- static KeyedService* BuildGCMProfileService(
- content::BrowserContext* context) {
- return new GCMProfileService(static_cast<Profile*>(context));
- }
-
- explicit GCMProfileServiceTestConsumer(Waiter* waiter)
- : waiter_(waiter),
- signin_manager_(NULL),
- gcm_client_loading_delay_(GCMClientMock::NO_DELAY_LOADING),
- registration_result_(GCMClient::UNKNOWN_ERROR),
- unregistration_result_(GCMClient::UNKNOWN_ERROR),
- send_result_(GCMClient::UNKNOWN_ERROR) {
- // Create a new profile.
- TestingProfile::Builder builder;
- builder.AddTestingFactory(SigninManagerFactory::GetInstance(),
- FakeSigninManager::Build);
- profile_ = builder.Build();
- signin_manager_ = static_cast<FakeSigninManager*>(
- SigninManagerFactory::GetInstance()->GetForProfile(profile_.get()));
-
- // Enable GCM such that tests could be run on all channels.
- profile()->GetPrefs()->SetBoolean(prefs::kGCMChannelEnabled, true);
- }
-
- virtual ~GCMProfileServiceTestConsumer() {
- GetGCMProfileService()->RemoveAppHandler(kTestingAppId);
- GetGCMProfileService()->RemoveAppHandler(kTestingAppId2);
- }
-
- void CreateGCMProfileServiceInstance() {
- GCMProfileService* gcm_profile_service = static_cast<GCMProfileService*>(
- GCMProfileServiceFactory::GetInstance()->SetTestingFactoryAndUse(
- profile(), &GCMProfileServiceTestConsumer::BuildGCMProfileService));
- scoped_ptr<GCMClientFactory> gcm_client_factory(
- new FakeGCMClientFactory(gcm_client_loading_delay_));
- gcm_profile_service->Initialize(gcm_client_factory.Pass());
-
- gcm_app_handler_.reset(new FakeGCMAppHandler(waiter_));
- gcm_profile_service->AddAppHandler(kTestingAppId, gcm_app_handler());
- gcm_profile_service->AddAppHandler(kTestingAppId2, gcm_app_handler());
-
- waiter_->PumpIOLoop();
- }
-
- void SignIn(const std::string& username) {
- signin_manager_->SignIn(username);
- waiter_->PumpIOLoop();
- waiter_->PumpUILoop();
- }
-
- void SignOut() {
- signin_manager_->SignOut();
- waiter_->PumpIOLoop();
- waiter_->PumpUILoop();
- }
-
- void Register(const std::string& app_id,
- const std::vector<std::string>& sender_ids) {
- GetGCMProfileService()->Register(
- app_id,
- sender_ids,
- base::Bind(&GCMProfileServiceTestConsumer::RegisterCompleted,
- base::Unretained(this)));
- }
-
- void RegisterCompleted(const std::string& registration_id,
- GCMClient::Result result) {
- registration_id_ = registration_id;
- registration_result_ = result;
- waiter_->SignalCompleted();
- }
-
- void Send(const std::string& app_id,
- const std::string& receiver_id,
- const GCMClient::OutgoingMessage& message) {
- GetGCMProfileService()->Send(
- app_id,
- receiver_id,
- message,
- base::Bind(&GCMProfileServiceTestConsumer::SendCompleted,
- base::Unretained(this)));
- }
-
- void SendCompleted(const std::string& message_id, GCMClient::Result result) {
- send_message_id_ = message_id;
- send_result_ = result;
- waiter_->SignalCompleted();
- }
-
- void Unregister(const std::string& app_id) {
- GetGCMProfileService()->Unregister(
- app_id,
- base::Bind(&GCMProfileServiceTestConsumer::UnregisterCompleted,
- base::Unretained(this)));
- }
-
- void UnregisterCompleted(GCMClient::Result result) {
- unregistration_result_ = result;
- waiter_->SignalCompleted();
- }
-
- GCMProfileService* GetGCMProfileService() const {
- return GCMProfileServiceFactory::GetForProfile(profile());
- }
-
- GCMClientMock* GetGCMClient() const {
- return static_cast<GCMClientMock*>(
- GetGCMProfileService()->GetGCMClientForTesting());
- }
-
- const std::string& GetUsername() const {
- return GetGCMProfileService()->username_;
- }
-
- bool IsGCMClientReady() const {
- return GetGCMProfileService()->gcm_client_ready_;
- }
-
- bool HasAppHandlers() const {
- return !GetGCMProfileService()->app_handlers_.empty();
- }
+class GCMProfileServiceTest : public testing::Test {
+ protected:
+ GCMProfileServiceTest();
+ virtual ~GCMProfileServiceTest();
- Profile* profile() const { return profile_.get(); }
- FakeGCMAppHandler* gcm_app_handler() const {
- return gcm_app_handler_.get();
- }
+ // testing::Test:
+ virtual void SetUp() OVERRIDE;
- void set_gcm_client_loading_delay(GCMClientMock::LoadingDelay delay) {
- gcm_client_loading_delay_ = delay;
- }
+ GCMClientMock* GetGCMClient() const;
- const std::string& registration_id() const { return registration_id_; }
- GCMClient::Result registration_result() const { return registration_result_; }
- GCMClient::Result unregistration_result() const {
- return unregistration_result_;
- }
- const std::string& send_message_id() const { return send_message_id_; }
- GCMClient::Result send_result() const { return send_result_; }
+ void RegisterAndWaitForCompletion(const std::vector<std::string>& sender_ids);
+ void SendAndWaitForCompletion(const GCMClient::OutgoingMessage& message);
- void clear_registration_result() {
- registration_id_.clear();
- registration_result_ = GCMClient::UNKNOWN_ERROR;
- }
- void clear_unregistration_result() {
- unregistration_result_ = GCMClient::UNKNOWN_ERROR;
- }
- void clear_send_result() {
- send_message_id_.clear();
- send_result_ = GCMClient::UNKNOWN_ERROR;
- }
+ void RegisterCompleted(const base::Closure& callback,
+ const std::string& registration_id,
+ GCMClient::Result result);
+ void SendCompleted(const base::Closure& callback,
+ const std::string& message_id,
+ GCMClient::Result result);
- private:
- Waiter* waiter_; // Not owned.
+ content::TestBrowserThreadBundle thread_bundle_;
scoped_ptr<TestingProfile> profile_;
- FakeSigninManager* signin_manager_; // Not owned.
- scoped_ptr<FakeGCMAppHandler> gcm_app_handler_;
-
- GCMClientMock::LoadingDelay gcm_client_loading_delay_;
+ GCMProfileService* gcm_profile_service_;
std::string registration_id_;
GCMClient::Result registration_result_;
-
- GCMClient::Result unregistration_result_;
-
std::string send_message_id_;
GCMClient::Result send_result_;
- DISALLOW_COPY_AND_ASSIGN(GCMProfileServiceTestConsumer);
-};
-
-class GCMProfileServiceTest : public testing::Test {
- public:
- GCMProfileServiceTest() {
- }
-
- virtual ~GCMProfileServiceTest() {
- }
-
- // Overridden from test::Test:
- virtual void SetUp() OVERRIDE {
- // Make BrowserThread work in unittest.
- thread_bundle_.reset(new content::TestBrowserThreadBundle(
- content::TestBrowserThreadBundle::REAL_IO_THREAD));
-
- // Create a main profile consumer.
- consumer_.reset(new GCMProfileServiceTestConsumer(&waiter_));
- }
-
- virtual void TearDown() OVERRIDE {
- consumer_.reset();
- PumpUILoop();
- }
-
- void WaitUntilCompleted() {
- waiter_.WaitUntilCompleted();
- }
-
- void SignalCompleted() {
- waiter_.SignalCompleted();
- }
-
- void PumpUILoop() {
- waiter_.PumpUILoop();
- }
-
- void PumpIOLoop() {
- waiter_.PumpIOLoop();
- }
-
- Profile* profile() const { return consumer_->profile(); }
- GCMProfileServiceTestConsumer* consumer() const { return consumer_.get(); }
-
- protected:
- Waiter waiter_;
-
private:
- scoped_ptr<content::TestBrowserThreadBundle> thread_bundle_;
- scoped_ptr<GCMProfileServiceTestConsumer> consumer_;
-
DISALLOW_COPY_AND_ASSIGN(GCMProfileServiceTest);
};
-TEST_F(GCMProfileServiceTest, Incognito) {
- EXPECT_TRUE(GCMProfileServiceFactory::GetForProfile(profile()));
-
- // Create an incognito profile.
- TestingProfile::Builder incognito_profile_builder;
- incognito_profile_builder.SetIncognito();
- scoped_ptr<TestingProfile> incognito_profile =
- incognito_profile_builder.Build();
- incognito_profile->SetOriginalProfile(profile());
-
- EXPECT_FALSE(GCMProfileServiceFactory::GetForProfile(
- incognito_profile.get()));
+GCMProfileServiceTest::GCMProfileServiceTest()
+ : gcm_profile_service_(NULL),
+ registration_result_(GCMClient::UNKNOWN_ERROR),
+ send_result_(GCMClient::UNKNOWN_ERROR) {
}
-TEST_F(GCMProfileServiceTest, CreateGCMProfileServiceBeforeProfileSignIn) {
- // Create GCMProfileService first.
- consumer()->CreateGCMProfileServiceInstance();
- EXPECT_TRUE(consumer()->GetUsername().empty());
-
- // Sign in to a profile. This will kick off the check-in.
- consumer()->SignIn(kTestingUsername);
- EXPECT_FALSE(consumer()->GetUsername().empty());
+GCMProfileServiceTest::~GCMProfileServiceTest() {
}
-TEST_F(GCMProfileServiceTest, CreateGCMProfileServiceAfterProfileSignIn) {
- // Sign in to a profile. This will not initiate the check-in.
- consumer()->SignIn(kTestingUsername);
-
- // Create GCMProfileService after sign-in.
- consumer()->CreateGCMProfileServiceInstance();
- EXPECT_FALSE(consumer()->GetUsername().empty());
+GCMClientMock* GCMProfileServiceTest::GetGCMClient() const {
+ return static_cast<GCMClientMock*>(
+ gcm_profile_service_->GetGCMClientForTesting());
}
-TEST_F(GCMProfileServiceTest, Shutdown) {
- consumer()->CreateGCMProfileServiceInstance();
- EXPECT_TRUE(consumer()->HasAppHandlers());
-
- consumer()->GetGCMProfileService()->Shutdown();
- EXPECT_FALSE(consumer()->HasAppHandlers());
-}
+void GCMProfileServiceTest::SetUp() {
+ TestingProfile::Builder builder;
+ builder.AddTestingFactory(SigninManagerFactory::GetInstance(),
+ FakeSigninManager::Build);
+ profile_ = builder.Build();
-TEST_F(GCMProfileServiceTest, SignInAndSignOutUnderPositiveChannelSignal) {
- // Positive channel signal is provided in SetUp.
- consumer()->CreateGCMProfileServiceInstance();
- consumer()->SignIn(kTestingUsername);
+ gcm_profile_service_ = static_cast<GCMProfileService*>(
+ GCMProfileServiceFactory::GetInstance()->SetTestingFactoryAndUse(
+ profile_.get(),
+ &BuildGCMProfileService));
+ gcm_profile_service_->Initialize(scoped_ptr<GCMClientFactory>(
+ new FakeGCMClientFactory(GCMClientMock::NO_DELAY_LOADING)));
- // GCMClient should be loaded.
- EXPECT_TRUE(consumer()->IsGCMClientReady());
- EXPECT_EQ(GCMClientMock::LOADED, consumer()->GetGCMClient()->status());
-
- consumer()->SignOut();
-
- // GCMClient should be checked out.
- EXPECT_FALSE(consumer()->IsGCMClientReady());
- EXPECT_EQ(GCMClientMock::CHECKED_OUT, consumer()->GetGCMClient()->status());
+ FakeSigninManager* signin_manager = static_cast<FakeSigninManager*>(
+ SigninManagerFactory::GetInstance()->GetForProfile(profile_.get()));
+ signin_manager->SignIn(kTestAccountID);
+ base::RunLoop().RunUntilIdle();
}
-TEST_F(GCMProfileServiceTest, SignInAndSignOutUnderNegativeChannelSignal) {
- // Negative channel signal will prevent GCMClient from checking in when the
- // profile is signed in.
- profile()->GetPrefs()->SetBoolean(prefs::kGCMChannelEnabled, false);
-
- consumer()->CreateGCMProfileServiceInstance();
- consumer()->SignIn(kTestingUsername);
-
- // GCMClient should not be loaded.
- EXPECT_FALSE(consumer()->IsGCMClientReady());
- EXPECT_EQ(GCMClientMock::UNINITIALIZED, consumer()->GetGCMClient()->status());
-
- consumer()->SignOut();
-
- // Check-out should still be performed.
- EXPECT_FALSE(consumer()->IsGCMClientReady());
- EXPECT_EQ(GCMClientMock::CHECKED_OUT, consumer()->GetGCMClient()->status());
+void GCMProfileServiceTest::RegisterAndWaitForCompletion(
+ const std::vector<std::string>& sender_ids) {
+ base::RunLoop run_loop;
+ gcm_profile_service_->Register(
+ kTestAppID,
+ sender_ids,
+ base::Bind(&GCMProfileServiceTest::RegisterCompleted,
+ base::Unretained(this),
+ run_loop.QuitClosure()));
+ run_loop.Run();
}
-TEST_F(GCMProfileServiceTest, SignOutAndThenSignIn) {
- // Positive channel signal is provided in SetUp.
- consumer()->CreateGCMProfileServiceInstance();
- consumer()->SignIn(kTestingUsername);
-
- // GCMClient should be loaded.
- EXPECT_TRUE(consumer()->IsGCMClientReady());
- EXPECT_EQ(GCMClientMock::LOADED, consumer()->GetGCMClient()->status());
-
- consumer()->SignOut();
-
- // GCMClient should be checked out.
- EXPECT_FALSE(consumer()->IsGCMClientReady());
- EXPECT_EQ(GCMClientMock::CHECKED_OUT, consumer()->GetGCMClient()->status());
-
- // Sign-in with a different username.
- consumer()->SignIn(kTestingUsername2);
-
- // GCMClient should be loaded again.
- EXPECT_TRUE(consumer()->IsGCMClientReady());
- EXPECT_EQ(GCMClientMock::LOADED, consumer()->GetGCMClient()->status());
+void GCMProfileServiceTest::SendAndWaitForCompletion(
+ const GCMClient::OutgoingMessage& message) {
+ base::RunLoop run_loop;
+ gcm_profile_service_->Send(kTestAppID,
+ kUserID,
+ message,
+ base::Bind(&GCMProfileServiceTest::SendCompleted,
+ base::Unretained(this),
+ run_loop.QuitClosure()));
+ run_loop.Run();
}
-TEST_F(GCMProfileServiceTest, StopAndRestartGCM) {
- // Positive channel signal is provided in SetUp.
- consumer()->CreateGCMProfileServiceInstance();
- consumer()->SignIn(kTestingUsername);
-
- // GCMClient should be loaded.
- EXPECT_TRUE(consumer()->IsGCMClientReady());
- EXPECT_EQ(GCMClientMock::LOADED, consumer()->GetGCMClient()->status());
-
- // Stops the GCM.
- consumer()->GetGCMProfileService()->Stop();
- PumpIOLoop();
-
- // GCMClient should be stopped.
- EXPECT_FALSE(consumer()->IsGCMClientReady());
- EXPECT_EQ(GCMClientMock::STOPPED, consumer()->GetGCMClient()->status());
-
- // Restarts the GCM.
- consumer()->GetGCMProfileService()->Start();
- PumpIOLoop();
-
- // GCMClient should be loaded.
- EXPECT_TRUE(consumer()->IsGCMClientReady());
- EXPECT_EQ(GCMClientMock::LOADED, consumer()->GetGCMClient()->status());
-
- // Stops the GCM.
- consumer()->GetGCMProfileService()->Stop();
- PumpIOLoop();
-
- // GCMClient should be stopped.
- EXPECT_FALSE(consumer()->IsGCMClientReady());
- EXPECT_EQ(GCMClientMock::STOPPED, consumer()->GetGCMClient()->status());
-
- // Signs out.
- consumer()->SignOut();
-
- // GCMClient should be checked out.
- EXPECT_FALSE(consumer()->IsGCMClientReady());
- EXPECT_EQ(GCMClientMock::CHECKED_OUT, consumer()->GetGCMClient()->status());
+void GCMProfileServiceTest::RegisterCompleted(
+ const base::Closure& callback,
+ const std::string& registration_id,
+ GCMClient::Result result) {
+ registration_id_ = registration_id;
+ registration_result_ = result;
+ callback.Run();
}
-TEST_F(GCMProfileServiceTest, RegisterWhenNotSignedIn) {
- consumer()->CreateGCMProfileServiceInstance();
-
- std::vector<std::string> sender_ids;
- sender_ids.push_back("sender1");
- consumer()->Register(kTestingAppId, sender_ids);
-
- EXPECT_TRUE(consumer()->registration_id().empty());
- EXPECT_EQ(GCMClient::NOT_SIGNED_IN, consumer()->registration_result());
+void GCMProfileServiceTest::SendCompleted(
+ const base::Closure& callback,
+ const std::string& message_id,
+ GCMClient::Result result) {
+ send_message_id_ = message_id;
+ send_result_ = result;
+ callback.Run();
}
TEST_F(GCMProfileServiceTest, RegisterUnderNeutralChannelSignal) {
- // Neutral channel signal will prevent GCMClient from checking in when the
- // profile is signed in.
- profile()->GetPrefs()->ClearPref(prefs::kGCMChannelEnabled);
-
- consumer()->CreateGCMProfileServiceInstance();
- consumer()->SignIn(kTestingUsername);
-
// GCMClient should not be checked in.
- EXPECT_FALSE(consumer()->IsGCMClientReady());
- EXPECT_EQ(GCMClientMock::UNINITIALIZED, consumer()->GetGCMClient()->status());
+ EXPECT_FALSE(gcm_profile_service_->IsGCMClientReady());
+ EXPECT_EQ(GCMClientMock::UNINITIALIZED, GetGCMClient()->status());
// Invoking register will make GCMClient checked in.
std::vector<std::string> sender_ids;
- sender_ids.push_back("sender1");
- consumer()->Register(kTestingAppId, sender_ids);
- WaitUntilCompleted();
+ sender_ids.push_back("sender");
+ RegisterAndWaitForCompletion(sender_ids);
// GCMClient should be checked in.
- EXPECT_TRUE(consumer()->IsGCMClientReady());
- EXPECT_EQ(GCMClientMock::LOADED, consumer()->GetGCMClient()->status());
+ EXPECT_TRUE(gcm_profile_service_->IsGCMClientReady());
+ EXPECT_EQ(GCMClientMock::LOADED, GetGCMClient()->status());
// Registration should succeed.
std::string expected_registration_id =
GCMClientMock::GetRegistrationIdFromSenderIds(sender_ids);
- EXPECT_EQ(expected_registration_id, consumer()->registration_id());
- EXPECT_EQ(GCMClient::SUCCESS, consumer()->registration_result());
-}
-
-TEST_F(GCMProfileServiceTest, SendWhenNotSignedIn) {
- consumer()->CreateGCMProfileServiceInstance();
-
- GCMClient::OutgoingMessage message;
- message.id = "1";
- message.data["key1"] = "value1";
- consumer()->Send(kTestingAppId, kUserId, message);
-
- EXPECT_TRUE(consumer()->send_message_id().empty());
- EXPECT_EQ(GCMClient::NOT_SIGNED_IN, consumer()->send_result());
+ EXPECT_EQ(expected_registration_id, registration_id_);
+ EXPECT_EQ(GCMClient::SUCCESS, registration_result_);
}
TEST_F(GCMProfileServiceTest, SendUnderNeutralChannelSignal) {
- // Neutral channel signal will prevent GCMClient from checking in when the
- // profile is signed in.
- profile()->GetPrefs()->ClearPref(prefs::kGCMChannelEnabled);
-
- consumer()->CreateGCMProfileServiceInstance();
- consumer()->SignIn(kTestingUsername);
-
// GCMClient should not be checked in.
- EXPECT_FALSE(consumer()->IsGCMClientReady());
- EXPECT_EQ(GCMClientMock::UNINITIALIZED, consumer()->GetGCMClient()->status());
+ EXPECT_FALSE(gcm_profile_service_->IsGCMClientReady());
+ EXPECT_EQ(GCMClientMock::UNINITIALIZED, GetGCMClient()->status());
// Invoking send will make GCMClient checked in.
GCMClient::OutgoingMessage message;
message.id = "1";
message.data["key1"] = "value1";
- consumer()->Send(kTestingAppId, kUserId, message);
- WaitUntilCompleted();
+ SendAndWaitForCompletion( message);
// GCMClient should be checked in.
- EXPECT_TRUE(consumer()->IsGCMClientReady());
- EXPECT_EQ(GCMClientMock::LOADED, consumer()->GetGCMClient()->status());
+ EXPECT_TRUE(gcm_profile_service_->IsGCMClientReady());
+ EXPECT_EQ(GCMClientMock::LOADED, GetGCMClient()->status());
// Sending should succeed.
- EXPECT_EQ(consumer()->send_message_id(), message.id);
- EXPECT_EQ(GCMClient::SUCCESS, consumer()->send_result());
-}
-
-// Tests single-profile.
-class GCMProfileServiceSingleProfileTest : public GCMProfileServiceTest {
- public:
- GCMProfileServiceSingleProfileTest() {
- }
-
- virtual ~GCMProfileServiceSingleProfileTest() {
- }
-
- virtual void SetUp() OVERRIDE {
- GCMProfileServiceTest::SetUp();
-
- consumer()->CreateGCMProfileServiceInstance();
- consumer()->SignIn(kTestingUsername);
- }
-};
-
-TEST_F(GCMProfileServiceSingleProfileTest, Register) {
- std::vector<std::string> sender_ids;
- sender_ids.push_back("sender1");
- consumer()->Register(kTestingAppId, sender_ids);
- std::string expected_registration_id =
- GCMClientMock::GetRegistrationIdFromSenderIds(sender_ids);
-
- WaitUntilCompleted();
- EXPECT_EQ(expected_registration_id, consumer()->registration_id());
- EXPECT_EQ(GCMClient::SUCCESS, consumer()->registration_result());
-}
-
-TEST_F(GCMProfileServiceSingleProfileTest, RegisterError) {
- std::vector<std::string> sender_ids;
- sender_ids.push_back("sender1@error");
- consumer()->Register(kTestingAppId, sender_ids);
-
- WaitUntilCompleted();
- EXPECT_TRUE(consumer()->registration_id().empty());
- EXPECT_NE(GCMClient::SUCCESS, consumer()->registration_result());
-}
-
-TEST_F(GCMProfileServiceSingleProfileTest, RegisterAgainWithSameSenderIDs) {
- std::vector<std::string> sender_ids;
- sender_ids.push_back("sender1");
- sender_ids.push_back("sender2");
- consumer()->Register(kTestingAppId, sender_ids);
- std::string expected_registration_id =
- GCMClientMock::GetRegistrationIdFromSenderIds(sender_ids);
-
- WaitUntilCompleted();
- EXPECT_EQ(expected_registration_id, consumer()->registration_id());
- EXPECT_EQ(GCMClient::SUCCESS, consumer()->registration_result());
-
- // Clears the results the would be set by the Register callback in preparation
- // to call register 2nd time.
- consumer()->clear_registration_result();
-
- // Calling register 2nd time with the same set of sender IDs but different
- // ordering will get back the same registration ID.
- std::vector<std::string> another_sender_ids;
- another_sender_ids.push_back("sender2");
- another_sender_ids.push_back("sender1");
- consumer()->Register(kTestingAppId, another_sender_ids);
-
- WaitUntilCompleted();
- EXPECT_EQ(expected_registration_id, consumer()->registration_id());
- EXPECT_EQ(GCMClient::SUCCESS, consumer()->registration_result());
-}
-
-TEST_F(GCMProfileServiceSingleProfileTest,
- RegisterAgainWithDifferentSenderIDs) {
- std::vector<std::string> sender_ids;
- sender_ids.push_back("sender1");
- consumer()->Register(kTestingAppId, sender_ids);
- std::string expected_registration_id =
- GCMClientMock::GetRegistrationIdFromSenderIds(sender_ids);
-
- WaitUntilCompleted();
- EXPECT_EQ(expected_registration_id, consumer()->registration_id());
- EXPECT_EQ(GCMClient::SUCCESS, consumer()->registration_result());
-
- // Make sender IDs different.
- sender_ids.push_back("sender2");
- std::string expected_registration_id2 =
- GCMClientMock::GetRegistrationIdFromSenderIds(sender_ids);
-
- // Calling register 2nd time with the different sender IDs will get back a new
- // registration ID.
- consumer()->Register(kTestingAppId, sender_ids);
- WaitUntilCompleted();
- EXPECT_EQ(expected_registration_id2, consumer()->registration_id());
- EXPECT_EQ(GCMClient::SUCCESS, consumer()->registration_result());
-}
-
-TEST_F(GCMProfileServiceSingleProfileTest,
- GCMClientNotReadyBeforeRegistration) {
- // Make GCMClient not ready initially.
- consumer()->set_gcm_client_loading_delay(GCMClientMock::DELAY_LOADING);
- consumer()->CreateGCMProfileServiceInstance();
-
- // The registration is on hold until GCMClient is ready.
- std::vector<std::string> sender_ids;
- sender_ids.push_back("sender1");
- consumer()->Register(kTestingAppId, sender_ids);
- PumpIOLoop();
- EXPECT_TRUE(consumer()->registration_id().empty());
- EXPECT_EQ(GCMClient::UNKNOWN_ERROR, consumer()->registration_result());
-
- // Register operation will be invoked after GCMClient becomes ready.
- consumer()->GetGCMClient()->PerformDelayedLoading();
- PumpIOLoop(); // The 1st pump is to wait till the delayed loading is done.
- PumpIOLoop(); // The 2nd pump is to wait till the registration is done.
- EXPECT_FALSE(consumer()->registration_id().empty());
- EXPECT_EQ(GCMClient::SUCCESS, consumer()->registration_result());
-}
-
-TEST_F(GCMProfileServiceSingleProfileTest, RegisterAfterSignOut) {
- // This will trigger check-out.
- consumer()->SignOut();
-
- std::vector<std::string> sender_ids;
- sender_ids.push_back("sender1");
- consumer()->Register(kTestingAppId, sender_ids);
-
- EXPECT_TRUE(consumer()->registration_id().empty());
- EXPECT_EQ(GCMClient::NOT_SIGNED_IN, consumer()->registration_result());
-}
-
-TEST_F(GCMProfileServiceSingleProfileTest, UnregisterExplicitly) {
- std::vector<std::string> sender_ids;
- sender_ids.push_back("sender1");
- consumer()->Register(kTestingAppId, sender_ids);
-
- WaitUntilCompleted();
- EXPECT_FALSE(consumer()->registration_id().empty());
- EXPECT_EQ(GCMClient::SUCCESS, consumer()->registration_result());
-
- consumer()->Unregister(kTestingAppId);
-
- WaitUntilCompleted();
- EXPECT_EQ(GCMClient::SUCCESS, consumer()->unregistration_result());
-}
-
-TEST_F(GCMProfileServiceSingleProfileTest,
- UnregisterWhenAsyncOperationPending) {
- std::vector<std::string> sender_ids;
- sender_ids.push_back("sender1");
- // First start registration without waiting for it to complete.
- consumer()->Register(kTestingAppId, sender_ids);
-
- // Test that unregistration fails with async operation pending when there is a
- // registration already in progress.
- consumer()->Unregister(kTestingAppId);
- EXPECT_EQ(GCMClient::ASYNC_OPERATION_PENDING,
- consumer()->unregistration_result());
-
- // Complete the registration.
- WaitUntilCompleted();
- EXPECT_EQ(GCMClient::SUCCESS, consumer()->registration_result());
-
- // Start unregistration without waiting for it to complete. This time no async
- // operation is pending.
- consumer()->Unregister(kTestingAppId);
-
- // Test that unregistration fails with async operation pending when there is
- // an unregistration already in progress.
- consumer()->Unregister(kTestingAppId);
- EXPECT_EQ(GCMClient::ASYNC_OPERATION_PENDING,
- consumer()->unregistration_result());
- consumer()->clear_unregistration_result();
-
- // Complete unregistration.
- WaitUntilCompleted();
- EXPECT_EQ(GCMClient::SUCCESS, consumer()->unregistration_result());
-}
-
-TEST_F(GCMProfileServiceSingleProfileTest, RegisterWhenAsyncOperationPending) {
- std::vector<std::string> sender_ids;
- sender_ids.push_back("sender1");
- // First start registration without waiting for it to complete.
- consumer()->Register(kTestingAppId, sender_ids);
-
- // Test that registration fails with async operation pending when there is a
- // registration already in progress.
- consumer()->Register(kTestingAppId, sender_ids);
- EXPECT_EQ(GCMClient::ASYNC_OPERATION_PENDING,
- consumer()->registration_result());
- consumer()->clear_registration_result();
-
- // Complete the registration.
- WaitUntilCompleted();
- EXPECT_EQ(GCMClient::SUCCESS, consumer()->registration_result());
-
- // Start unregistration without waiting for it to complete. This time no async
- // operation is pending.
- consumer()->Unregister(kTestingAppId);
-
- // Test that registration fails with async operation pending when there is an
- // unregistration already in progress.
- consumer()->Register(kTestingAppId, sender_ids);
- EXPECT_EQ(GCMClient::ASYNC_OPERATION_PENDING,
- consumer()->registration_result());
-
- // Complete the first unregistration expecting success.
- WaitUntilCompleted();
- EXPECT_EQ(GCMClient::SUCCESS, consumer()->unregistration_result());
-
- // Test that it is ok to register again after unregistration.
- consumer()->Register(kTestingAppId, sender_ids);
- WaitUntilCompleted();
- EXPECT_EQ(GCMClient::SUCCESS, consumer()->registration_result());
-}
-
-
-TEST_F(GCMProfileServiceSingleProfileTest, Send) {
- GCMClient::OutgoingMessage message;
- message.id = "1";
- message.data["key1"] = "value1";
- message.data["key2"] = "value2";
- consumer()->Send(kTestingAppId, kUserId, message);
-
- // Wait for the send callback is called.
- WaitUntilCompleted();
- EXPECT_EQ(consumer()->send_message_id(), message.id);
- EXPECT_EQ(GCMClient::SUCCESS, consumer()->send_result());
-}
-
-TEST_F(GCMProfileServiceSingleProfileTest, GCMClientNotReadyBeforeSending) {
- // Make GCMClient not ready initially.
- consumer()->set_gcm_client_loading_delay(GCMClientMock::DELAY_LOADING);
- consumer()->CreateGCMProfileServiceInstance();
-
- // The sending is on hold until GCMClient is ready.
- GCMClient::OutgoingMessage message;
- message.id = "1";
- message.data["key1"] = "value1";
- message.data["key2"] = "value2";
- consumer()->Send(kTestingAppId, kUserId, message);
- PumpIOLoop();
- EXPECT_TRUE(consumer()->send_message_id().empty());
- EXPECT_EQ(GCMClient::UNKNOWN_ERROR, consumer()->send_result());
-
- // Register operation will be invoked after GCMClient becomes ready.
- consumer()->GetGCMClient()->PerformDelayedLoading();
- PumpIOLoop();
- PumpIOLoop();
- EXPECT_EQ(consumer()->send_message_id(), message.id);
- EXPECT_EQ(GCMClient::SUCCESS, consumer()->send_result());
-}
-
-TEST_F(GCMProfileServiceSingleProfileTest, SendAfterSignOut) {
- // This will trigger check-out.
- consumer()->SignOut();
-
- GCMClient::OutgoingMessage message;
- message.id = "1";
- message.data["key1"] = "value1";
- message.data["key2"] = "value2";
- consumer()->Send(kTestingAppId, kUserId, message);
-
- EXPECT_TRUE(consumer()->send_message_id().empty());
- EXPECT_EQ(GCMClient::NOT_SIGNED_IN, consumer()->send_result());
-}
-
-TEST_F(GCMProfileServiceSingleProfileTest, SendError) {
- GCMClient::OutgoingMessage message;
- // Embedding error in id will tell the mock to simulate the send error.
- message.id = "1@error";
- message.data["key1"] = "value1";
- message.data["key2"] = "value2";
- consumer()->Send(kTestingAppId, kUserId, message);
-
- // Wait for the send callback is called.
- WaitUntilCompleted();
- EXPECT_EQ(consumer()->send_message_id(), message.id);
- EXPECT_EQ(GCMClient::SUCCESS, consumer()->send_result());
-
- // Wait for the send error.
- WaitUntilCompleted();
- EXPECT_EQ(FakeGCMAppHandler::SEND_ERROR_EVENT,
- consumer()->gcm_app_handler()->received_event());
- EXPECT_EQ(kTestingAppId, consumer()->gcm_app_handler()->app_id());
- EXPECT_EQ(consumer()->send_message_id(),
- consumer()->gcm_app_handler()->send_error_message_id());
- EXPECT_NE(GCMClient::SUCCESS,
- consumer()->gcm_app_handler()->send_error_result());
- EXPECT_EQ(message.data, consumer()->gcm_app_handler()->send_error_data());
-}
-
-TEST_F(GCMProfileServiceSingleProfileTest, MessageReceived) {
- consumer()->Register(kTestingAppId, ToSenderList("sender"));
- WaitUntilCompleted();
- GCMClient::IncomingMessage message;
- message.data["key1"] = "value1";
- message.data["key2"] = "value2";
- message.sender_id = "sender";
- consumer()->GetGCMClient()->ReceiveMessage(kTestingAppId, message);
- WaitUntilCompleted();
- EXPECT_EQ(FakeGCMAppHandler::MESSAGE_EVENT,
- consumer()->gcm_app_handler()->received_event());
- EXPECT_EQ(kTestingAppId, consumer()->gcm_app_handler()->app_id());
- EXPECT_TRUE(message.data == consumer()->gcm_app_handler()->message().data);
- EXPECT_TRUE(consumer()->gcm_app_handler()->message().collapse_key.empty());
- EXPECT_EQ(message.sender_id,
- consumer()->gcm_app_handler()->message().sender_id);
-}
-
-TEST_F(GCMProfileServiceSingleProfileTest, MessageWithCollapseKeyReceived) {
- consumer()->Register(kTestingAppId, ToSenderList("sender"));
- WaitUntilCompleted();
- GCMClient::IncomingMessage message;
- message.data["key1"] = "value1";
- message.collapse_key = "collapse_key_value";
- message.sender_id = "sender";
- consumer()->GetGCMClient()->ReceiveMessage(kTestingAppId, message);
- WaitUntilCompleted();
- EXPECT_EQ(FakeGCMAppHandler::MESSAGE_EVENT,
- consumer()->gcm_app_handler()->received_event());
- EXPECT_EQ(kTestingAppId, consumer()->gcm_app_handler()->app_id());
- EXPECT_TRUE(message.data == consumer()->gcm_app_handler()->message().data);
- EXPECT_EQ(message.collapse_key,
- consumer()->gcm_app_handler()->message().collapse_key);
-}
-
-TEST_F(GCMProfileServiceSingleProfileTest, MessagesDeleted) {
- consumer()->GetGCMClient()->DeleteMessages(kTestingAppId);
- WaitUntilCompleted();
- EXPECT_EQ(FakeGCMAppHandler::MESSAGES_DELETED_EVENT,
- consumer()->gcm_app_handler()->received_event());
- EXPECT_EQ(kTestingAppId, consumer()->gcm_app_handler()->app_id());
-}
-
-// Tests to make sure that GCMProfileService works for multiple profiles
-// regardless how GCMClient is created.
-class GCMProfileServiceMultiProfileTest : public GCMProfileServiceTest {
- public:
- GCMProfileServiceMultiProfileTest() {
- }
-
- virtual ~GCMProfileServiceMultiProfileTest() {
- }
-
- virtual void SetUp() OVERRIDE {
- GCMProfileServiceTest::SetUp();
-
- // Create a 2nd profile consumer.
- consumer2_.reset(new GCMProfileServiceTestConsumer(&waiter_));
-
- consumer()->CreateGCMProfileServiceInstance();
- consumer2()->CreateGCMProfileServiceInstance();
-
- // Initiate check-in for each profile.
- consumer2()->SignIn(kTestingUsername2);
- consumer()->SignIn(kTestingUsername);
- }
-
- virtual void TearDown() OVERRIDE {
- consumer2_.reset();
-
- GCMProfileServiceTest::TearDown();
- }
-
- Profile* profile2() const { return consumer2_->profile(); }
- GCMProfileServiceTestConsumer* consumer2() const { return consumer2_.get(); }
-
- protected:
- scoped_ptr<GCMProfileServiceTestConsumer> consumer2_;
-};
-
-TEST_F(GCMProfileServiceMultiProfileTest, Register) {
- // Register an app.
- std::vector<std::string> sender_ids;
- sender_ids.push_back("sender1");
- consumer()->Register(kTestingAppId, sender_ids);
-
- // Register the same app in a different profile.
- std::vector<std::string> sender_ids2;
- sender_ids2.push_back("foo");
- sender_ids2.push_back("bar");
- consumer2()->Register(kTestingAppId, sender_ids2);
-
- WaitUntilCompleted();
- WaitUntilCompleted();
-
- EXPECT_EQ(GCMClientMock::GetRegistrationIdFromSenderIds(sender_ids),
- consumer()->registration_id());
- EXPECT_EQ(GCMClient::SUCCESS, consumer()->registration_result());
-
- EXPECT_EQ(GCMClientMock::GetRegistrationIdFromSenderIds(sender_ids2),
- consumer2()->registration_id());
- EXPECT_EQ(GCMClient::SUCCESS, consumer()->registration_result());
-
- // Register a different app in a different profile.
- std::vector<std::string> sender_ids3;
- sender_ids3.push_back("sender1");
- sender_ids3.push_back("sender2");
- sender_ids3.push_back("sender3");
- consumer2()->Register(kTestingAppId2, sender_ids3);
-
- WaitUntilCompleted();
-
- EXPECT_EQ(GCMClientMock::GetRegistrationIdFromSenderIds(sender_ids3),
- consumer2()->registration_id());
- EXPECT_EQ(GCMClient::SUCCESS, consumer2()->registration_result());
-}
-
-TEST_F(GCMProfileServiceMultiProfileTest, Send) {
- // Send a message from one app in one profile.
- GCMClient::OutgoingMessage message;
- message.id = "1";
- message.data["key1"] = "value1";
- message.data["key2"] = "value2";
- consumer()->Send(kTestingAppId, kUserId, message);
-
- // Send a message from same app in another profile.
- GCMClient::OutgoingMessage message2;
- message2.id = "2";
- message2.data["foo"] = "bar";
- consumer2()->Send(kTestingAppId, kUserId2, message2);
-
- WaitUntilCompleted();
- WaitUntilCompleted();
-
- EXPECT_EQ(consumer()->send_message_id(), message.id);
- EXPECT_EQ(GCMClient::SUCCESS, consumer()->send_result());
-
- EXPECT_EQ(consumer2()->send_message_id(), message2.id);
- EXPECT_EQ(GCMClient::SUCCESS, consumer2()->send_result());
-
- // Send another message from different app in another profile.
- GCMClient::OutgoingMessage message3;
- message3.id = "3";
- message3.data["hello"] = "world";
- consumer2()->Send(kTestingAppId2, kUserId, message3);
-
- WaitUntilCompleted();
-
- EXPECT_EQ(consumer2()->send_message_id(), message3.id);
- EXPECT_EQ(GCMClient::SUCCESS, consumer()->send_result());
-}
-
-TEST_F(GCMProfileServiceMultiProfileTest, MessageReceived) {
- consumer()->Register(kTestingAppId, ToSenderList("sender"));
- WaitUntilCompleted();
- consumer2()->Register(kTestingAppId, ToSenderList("sender"));
- WaitUntilCompleted();
- consumer2()->Register(kTestingAppId2, ToSenderList("sender2"));
- WaitUntilCompleted();
-
- // Trigger an incoming message for an app in one profile.
- GCMClient::IncomingMessage message;
- message.data["key1"] = "value1";
- message.data["key2"] = "value2";
- message.sender_id = "sender";
- consumer()->GetGCMClient()->ReceiveMessage(kTestingAppId, message);
-
- // Trigger an incoming message for the same app in another profile.
- GCMClient::IncomingMessage message2;
- message2.data["foo"] = "bar";
- message2.sender_id = "sender";
- consumer2()->GetGCMClient()->ReceiveMessage(kTestingAppId, message2);
-
- WaitUntilCompleted();
- WaitUntilCompleted();
-
- EXPECT_EQ(FakeGCMAppHandler::MESSAGE_EVENT,
- consumer()->gcm_app_handler()->received_event());
- EXPECT_EQ(kTestingAppId, consumer()->gcm_app_handler()->app_id());
- EXPECT_TRUE(message.data == consumer()->gcm_app_handler()->message().data);
- EXPECT_EQ("sender", consumer()->gcm_app_handler()->message().sender_id);
-
- EXPECT_EQ(FakeGCMAppHandler::MESSAGE_EVENT,
- consumer2()->gcm_app_handler()->received_event());
- EXPECT_EQ(kTestingAppId, consumer2()->gcm_app_handler()->app_id());
- EXPECT_TRUE(message2.data == consumer2()->gcm_app_handler()->message().data);
- EXPECT_EQ("sender", consumer2()->gcm_app_handler()->message().sender_id);
-
- // Trigger another incoming message for a different app in another profile.
- GCMClient::IncomingMessage message3;
- message3.data["bar1"] = "foo1";
- message3.data["bar2"] = "foo2";
- message3.sender_id = "sender2";
- consumer2()->GetGCMClient()->ReceiveMessage(kTestingAppId2, message3);
-
- WaitUntilCompleted();
-
- EXPECT_EQ(FakeGCMAppHandler::MESSAGE_EVENT,
- consumer2()->gcm_app_handler()->received_event());
- EXPECT_EQ(kTestingAppId2, consumer2()->gcm_app_handler()->app_id());
- EXPECT_TRUE(message3.data == consumer2()->gcm_app_handler()->message().data);
- EXPECT_EQ("sender2", consumer2()->gcm_app_handler()->message().sender_id);
-}
-
-// Test a set of GCM operations on multiple profiles.
-// 1) Register 1 app in profile1 and register 2 apps in profile2;
-// 2) Send a message from profile1;
-// 3) Receive a message to an app in profile1 and receive a message for each of
-// two apps in profile2;
-// 4) Send a message foe ach of two apps in profile2;
-// 5) Sign out of profile1.
-// 6) Register/send stops working for profile1;
-// 7) The app in profile2 could still receive these events;
-// 8) Sign into profile1 with a different user.
-// 9) The message to the new signed-in user will be routed.
-TEST_F(GCMProfileServiceMultiProfileTest, Combined) {
- // Register an app.
- std::vector<std::string> sender_ids;
- sender_ids.push_back("sender1");
- consumer()->Register(kTestingAppId, sender_ids);
-
- // Register the same app in a different profile.
- std::vector<std::string> sender_ids2;
- sender_ids2.push_back("foo");
- sender_ids2.push_back("bar");
- consumer2()->Register(kTestingAppId, sender_ids2);
-
- // Register a different app in a different profile.
- std::vector<std::string> sender_ids3;
- sender_ids3.push_back("sender1");
- sender_ids3.push_back("sender2");
- sender_ids3.push_back("sender3");
- consumer2()->Register(kTestingAppId2, sender_ids3);
-
- WaitUntilCompleted();
- WaitUntilCompleted();
- WaitUntilCompleted();
-
- EXPECT_EQ(GCMClientMock::GetRegistrationIdFromSenderIds(sender_ids),
- consumer()->registration_id());
- EXPECT_EQ(GCMClient::SUCCESS, consumer()->registration_result());
-
- EXPECT_EQ(GCMClientMock::GetRegistrationIdFromSenderIds(sender_ids3),
- consumer2()->registration_id());
- EXPECT_EQ(GCMClient::SUCCESS, consumer()->registration_result());
-
- // Send a message from one profile.
- GCMClient::OutgoingMessage out_message;
- out_message.id = "1";
- out_message.data["out1"] = "out_data1";
- out_message.data["out1_2"] = "out_data1_2";
- consumer()->Send(kTestingAppId, kUserId, out_message);
-
- WaitUntilCompleted();
-
- EXPECT_EQ(consumer()->send_message_id(), out_message.id);
- EXPECT_EQ(GCMClient::SUCCESS, consumer()->send_result());
-
- // Trigger an incoming message for an app in one profile.
- GCMClient::IncomingMessage in_message;
- in_message.data["in1"] = "in_data1";
- in_message.data["in1_2"] = "in_data1_2";
- in_message.sender_id = "sender1";
- consumer()->GetGCMClient()->ReceiveMessage(kTestingAppId, in_message);
-
- WaitUntilCompleted();
-
- EXPECT_EQ(FakeGCMAppHandler::MESSAGE_EVENT,
- consumer()->gcm_app_handler()->received_event());
- EXPECT_EQ(kTestingAppId, consumer()->gcm_app_handler()->app_id());
- EXPECT_TRUE(
- in_message.data == consumer()->gcm_app_handler()->message().data);
-
- // Trigger 2 incoming messages, one for each app respectively, in another
- // profile.
- GCMClient::IncomingMessage in_message2;
- in_message2.data["in2"] = "in_data2";
- in_message2.sender_id = "sender3";
- consumer2()->GetGCMClient()->ReceiveMessage(kTestingAppId2, in_message2);
-
- GCMClient::IncomingMessage in_message3;
- in_message3.data["in3"] = "in_data3";
- in_message3.data["in3_2"] = "in_data3_2";
- in_message3.sender_id = "foo";
- consumer2()->GetGCMClient()->ReceiveMessage(kTestingAppId, in_message3);
-
- consumer2()->gcm_app_handler()->clear_results();
- WaitUntilCompleted();
-
- EXPECT_EQ(FakeGCMAppHandler::MESSAGE_EVENT,
- consumer2()->gcm_app_handler()->received_event());
- EXPECT_EQ(kTestingAppId2, consumer2()->gcm_app_handler()->app_id());
- EXPECT_TRUE(
- in_message2.data == consumer2()->gcm_app_handler()->message().data);
-
- consumer2()->gcm_app_handler()->clear_results();
- WaitUntilCompleted();
-
- EXPECT_EQ(FakeGCMAppHandler::MESSAGE_EVENT,
- consumer2()->gcm_app_handler()->received_event());
- EXPECT_EQ(kTestingAppId, consumer2()->gcm_app_handler()->app_id());
- EXPECT_TRUE(
- in_message3.data == consumer2()->gcm_app_handler()->message().data);
-
- // Send two messages, one for each app respectively, from another profile.
- GCMClient::OutgoingMessage out_message2;
- out_message2.id = "2";
- out_message2.data["out2"] = "out_data2";
- consumer2()->Send(kTestingAppId, kUserId2, out_message2);
-
- GCMClient::OutgoingMessage out_message3;
- out_message3.id = "2";
- out_message3.data["out3"] = "out_data3";
- consumer2()->Send(kTestingAppId2, kUserId2, out_message3);
-
- WaitUntilCompleted();
-
- EXPECT_EQ(consumer2()->send_message_id(), out_message2.id);
- EXPECT_EQ(GCMClient::SUCCESS, consumer2()->send_result());
-
- WaitUntilCompleted();
-
- EXPECT_EQ(consumer2()->send_message_id(), out_message3.id);
- EXPECT_EQ(GCMClient::SUCCESS, consumer2()->send_result());
-
- // Sign out of one profile.
- consumer()->SignOut();
-
- // Register/send stops working for signed-out profile.
- consumer()->gcm_app_handler()->clear_results();
- consumer()->Register(kTestingAppId, sender_ids);
- EXPECT_TRUE(consumer()->registration_id().empty());
- EXPECT_EQ(GCMClient::NOT_SIGNED_IN, consumer()->registration_result());
-
- consumer()->gcm_app_handler()->clear_results();
- consumer()->Send(kTestingAppId2, kUserId2, out_message3);
- EXPECT_TRUE(consumer()->send_message_id().empty());
- EXPECT_EQ(GCMClient::NOT_SIGNED_IN, consumer()->send_result());
-
- // Deleted messages event will go through for another signed-in profile.
- consumer2()->GetGCMClient()->DeleteMessages(kTestingAppId2);
-
- consumer2()->gcm_app_handler()->clear_results();
- WaitUntilCompleted();
-
- EXPECT_EQ(FakeGCMAppHandler::MESSAGES_DELETED_EVENT,
- consumer2()->gcm_app_handler()->received_event());
- EXPECT_EQ(kTestingAppId2, consumer2()->gcm_app_handler()->app_id());
-
- // Send error event will go through for another signed-in profile.
- GCMClient::OutgoingMessage out_message4;
- out_message4.id = "1@error";
- out_message4.data["out4"] = "out_data4";
- consumer2()->Send(kTestingAppId, kUserId, out_message4);
-
- WaitUntilCompleted();
- EXPECT_EQ(consumer2()->send_message_id(), out_message4.id);
- EXPECT_EQ(GCMClient::SUCCESS, consumer2()->send_result());
-
- consumer2()->gcm_app_handler()->clear_results();
- WaitUntilCompleted();
- EXPECT_EQ(FakeGCMAppHandler::SEND_ERROR_EVENT,
- consumer2()->gcm_app_handler()->received_event());
- EXPECT_EQ(kTestingAppId, consumer2()->gcm_app_handler()->app_id());
- EXPECT_EQ(out_message4.id,
- consumer2()->gcm_app_handler()->send_error_message_id());
- EXPECT_NE(GCMClient::SUCCESS,
- consumer2()->gcm_app_handler()->send_error_result());
- EXPECT_EQ(out_message4.data,
- consumer2()->gcm_app_handler()->send_error_data());
-
- // Sign in with a different user.
- consumer()->SignIn(kTestingUsername3);
-
- // Signing out cleared all registrations, so we need to register again.
- consumer()->Register(kTestingAppId, ToSenderList("sender1"));
- WaitUntilCompleted();
-
- // Incoming message will go through for the new signed-in user.
- GCMClient::IncomingMessage in_message5;
- in_message5.data["in5"] = "in_data5";
- in_message5.sender_id = "sender1";
- consumer()->GetGCMClient()->ReceiveMessage(kTestingAppId, in_message5);
-
- consumer()->gcm_app_handler()->clear_results();
- WaitUntilCompleted();
-
- EXPECT_EQ(FakeGCMAppHandler::MESSAGE_EVENT,
- consumer()->gcm_app_handler()->received_event());
- EXPECT_TRUE(
- in_message5.data == consumer()->gcm_app_handler()->message().data);
+ EXPECT_EQ(message.id, send_message_id_);
+ EXPECT_EQ(GCMClient::SUCCESS, send_result_);
}
} // namespace gcm
diff --git a/chrome/browser/services/gcm/gcm_service.cc b/chrome/browser/services/gcm/gcm_service.cc
new file mode 100644
index 0000000..0b5c74d
--- /dev/null
+++ b/chrome/browser/services/gcm/gcm_service.cc
@@ -0,0 +1,850 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/services/gcm/gcm_service.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/sequenced_task_runner.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "chrome/browser/services/gcm/gcm_app_handler.h"
+#include "chrome/browser/services/gcm/gcm_client_factory.h"
+#include "chrome/common/chrome_version_info.h"
+#include "content/public/browser/browser_thread.h"
+#include "google_apis/gaia/oauth2_token_service.h"
+#include "google_apis/gcm/protocol/android_checkin.pb.h"
+#include "net/url_request/url_request_context_getter.h"
+
+namespace gcm {
+
+namespace {
+
+checkin_proto::ChromeBuildProto_Platform GetPlatform() {
+#if defined(OS_WIN)
+ return checkin_proto::ChromeBuildProto_Platform_PLATFORM_WIN;
+#elif defined(OS_MACOSX)
+ return checkin_proto::ChromeBuildProto_Platform_PLATFORM_MAC;
+#elif defined(OS_IOS)
+ return checkin_proto::ChromeBuildProto_Platform_PLATFORM_IOS;
+#elif defined(OS_CHROMEOS)
+ return checkin_proto::ChromeBuildProto_Platform_PLATFORM_CROS;
+#elif defined(OS_LINUX)
+ return checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
+#else
+ // For all other platforms, return as LINUX.
+ return checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
+#endif
+}
+
+std::string GetVersion() {
+ chrome::VersionInfo version_info;
+ return version_info.Version();
+}
+
+checkin_proto::ChromeBuildProto_Channel GetChannel() {
+ chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
+ switch (channel) {
+ case chrome::VersionInfo::CHANNEL_UNKNOWN:
+ return checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN;
+ case chrome::VersionInfo::CHANNEL_CANARY:
+ return checkin_proto::ChromeBuildProto_Channel_CHANNEL_CANARY;
+ case chrome::VersionInfo::CHANNEL_DEV:
+ return checkin_proto::ChromeBuildProto_Channel_CHANNEL_DEV;
+ case chrome::VersionInfo::CHANNEL_BETA:
+ return checkin_proto::ChromeBuildProto_Channel_CHANNEL_BETA;
+ case chrome::VersionInfo::CHANNEL_STABLE:
+ return checkin_proto::ChromeBuildProto_Channel_CHANNEL_STABLE;
+ default:
+ NOTREACHED();
+ return checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN;
+ };
+}
+
+} // namespace
+
+// Helper class to save tasks to run until we're ready to execute them.
+class GCMService::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);
+};
+
+GCMService::DelayedTaskController::DelayedTaskController() : ready_(false) {
+}
+
+GCMService::DelayedTaskController::~DelayedTaskController() {
+}
+
+void GCMService::DelayedTaskController::AddTask(const base::Closure& task) {
+ delayed_tasks_.push_back(task);
+}
+
+void GCMService::DelayedTaskController::SetReady() {
+ ready_ = true;
+ RunTasks();
+}
+
+bool GCMService::DelayedTaskController::CanRunTaskWithoutDelay() const {
+ return ready_;
+}
+
+void GCMService::DelayedTaskController::RunTasks() {
+ DCHECK(ready_);
+
+ for (size_t i = 0; i < delayed_tasks_.size(); ++i)
+ delayed_tasks_[i].Run();
+ delayed_tasks_.clear();
+}
+
+class GCMService::IOWorker : public GCMClient::Delegate {
+ public:
+ // Called on UI thread.
+ IOWorker();
+ 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;
+
+ // Called on IO thread.
+ void Initialize(scoped_ptr<GCMClientFactory> gcm_client_factory,
+ const base::FilePath& store_path,
+ const std::vector<std::string>& account_ids,
+ const scoped_refptr<net::URLRequestContextGetter>&
+ url_request_context_getter);
+ void Load(const base::WeakPtr<GCMService>& 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:
+ base::WeakPtr<GCMService> service_;
+
+ scoped_ptr<GCMClient> gcm_client_;
+
+ DISALLOW_COPY_AND_ASSIGN(IOWorker);
+};
+
+GCMService::IOWorker::IOWorker() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+}
+
+GCMService::IOWorker::~IOWorker() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+}
+
+void GCMService::IOWorker::Initialize(
+ scoped_ptr<GCMClientFactory> gcm_client_factory,
+ const base::FilePath& store_path,
+ const std::vector<std::string>& account_ids,
+ const scoped_refptr<net::URLRequestContextGetter>&
+ url_request_context_getter) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+
+ gcm_client_ = gcm_client_factory->BuildInstance().Pass();
+
+ checkin_proto::ChromeBuildProto chrome_build_proto;
+ chrome_build_proto.set_platform(GetPlatform());
+ chrome_build_proto.set_chrome_version(GetVersion());
+ chrome_build_proto.set_channel(GetChannel());
+
+ scoped_refptr<base::SequencedWorkerPool> worker_pool(
+ content::BrowserThread::GetBlockingPool());
+ scoped_refptr<base::SequencedTaskRunner> blocking_task_runner(
+ worker_pool->GetSequencedTaskRunnerWithShutdownBehavior(
+ worker_pool->GetSequenceToken(),
+ base::SequencedWorkerPool::SKIP_ON_SHUTDOWN));
+
+ gcm_client_->Initialize(chrome_build_proto,
+ store_path,
+ account_ids,
+ blocking_task_runner,
+ url_request_context_getter,
+ this);
+}
+
+void GCMService::IOWorker::OnRegisterFinished(
+ const std::string& app_id,
+ const std::string& registration_id,
+ GCMClient::Result result) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+
+ content::BrowserThread::PostTask(content::BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&GCMService::RegisterFinished,
+ service_,
+ app_id,
+ registration_id,
+ result));
+}
+
+void GCMService::IOWorker::OnUnregisterFinished(const std::string& app_id,
+ GCMClient::Result result) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+
+ content::BrowserThread::PostTask(
+ content::BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&GCMService::UnregisterFinished, service_, app_id, result));
+}
+
+void GCMService::IOWorker::OnSendFinished(const std::string& app_id,
+ const std::string& message_id,
+ GCMClient::Result result) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+
+ content::BrowserThread::PostTask(content::BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&GCMService::SendFinished,
+ service_,
+ app_id,
+ message_id,
+ result));
+}
+
+void GCMService::IOWorker::OnMessageReceived(
+ const std::string& app_id,
+ const GCMClient::IncomingMessage& message) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+
+ content::BrowserThread::PostTask(content::BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&GCMService::MessageReceived,
+ service_,
+ app_id,
+ message));
+}
+
+void GCMService::IOWorker::OnMessagesDeleted(const std::string& app_id) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+
+ content::BrowserThread::PostTask(content::BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&GCMService::MessagesDeleted,
+ service_,
+ app_id));
+}
+
+void GCMService::IOWorker::OnMessageSendError(
+ const std::string& app_id,
+ const GCMClient::SendErrorDetails& send_error_details) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+
+ content::BrowserThread::PostTask(content::BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&GCMService::MessageSendError,
+ service_,
+ app_id,
+ send_error_details));
+}
+
+void GCMService::IOWorker::OnGCMReady() {
+ content::BrowserThread::PostTask(content::BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&GCMService::GCMClientReady,
+ service_));
+}
+
+void GCMService::IOWorker::Load(const base::WeakPtr<GCMService>& service) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+
+ service_ = service;
+ gcm_client_->Load();
+}
+
+void GCMService::IOWorker::Stop() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+
+ gcm_client_->Stop();
+}
+
+void GCMService::IOWorker::CheckOut() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+
+ gcm_client_->CheckOut();
+
+ // Note that we still need to keep GCMClient instance alive since the
+ // GCMService may check in again.
+}
+
+void GCMService::IOWorker::Register(
+ const std::string& app_id,
+ const std::vector<std::string>& sender_ids) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+
+ gcm_client_->Register(app_id, sender_ids);
+}
+
+void GCMService::IOWorker::Unregister(const std::string& app_id) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+
+ gcm_client_->Unregister(app_id);
+}
+
+void GCMService::IOWorker::Send(const std::string& app_id,
+ const std::string& receiver_id,
+ const GCMClient::OutgoingMessage& message) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+
+ gcm_client_->Send(app_id, receiver_id, message);
+}
+
+void GCMService::IOWorker::GetGCMStatistics(bool clear_logs) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+ gcm::GCMClient::GCMStatistics stats;
+
+ if (gcm_client_.get()) {
+ if (clear_logs)
+ gcm_client_->ClearActivityLogs();
+ stats = gcm_client_->GetStatistics();
+ }
+
+ content::BrowserThread::PostTask(
+ content::BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&GCMService::GetGCMStatisticsFinished, service_, stats));
+}
+
+void GCMService::IOWorker::SetGCMRecording(bool recording) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+ gcm::GCMClient::GCMStatistics stats;
+
+ if (gcm_client_.get()) {
+ gcm_client_->SetRecording(recording);
+ stats = gcm_client_->GetStatistics();
+ stats.gcm_client_created = true;
+ }
+
+ content::BrowserThread::PostTask(
+ content::BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&GCMService::GetGCMStatisticsFinished, service_, stats));
+}
+
+GCMService::GCMService(scoped_ptr<IdentityProvider> identity_provider)
+ : identity_provider_(identity_provider.Pass()),
+ gcm_client_ready_(false),
+ weak_ptr_factory_(this) {
+}
+
+GCMService::~GCMService() {
+}
+
+void GCMService::Initialize(scoped_ptr<GCMClientFactory> gcm_client_factory) {
+ // 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.
+ DCHECK(!io_worker_);
+ io_worker_.reset(new IOWorker());
+ content::BrowserThread::PostTask(
+ content::BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&GCMService::IOWorker::Initialize,
+ base::Unretained(io_worker_.get()),
+ base::Passed(&gcm_client_factory),
+ GetStorePath(),
+ account_ids,
+ GetURLRequestContextGetter()));
+
+ // Load from the GCM store and initiate the GCM check-in if the rollout signal
+ // indicates yes.
+ if (ShouldStartAutomatically())
+ EnsureLoaded();
+
+ identity_provider_->AddObserver(this);
+}
+
+void GCMService::Start() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+ EnsureLoaded();
+}
+
+void GCMService::Stop() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+ // No need to stop GCM service if not started yet.
+ if (account_id_.empty())
+ return;
+
+ RemoveCachedData();
+
+ content::BrowserThread::PostTask(
+ content::BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&GCMService::IOWorker::Stop,
+ base::Unretained(io_worker_.get())));
+}
+
+void GCMService::ShutdownService() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ identity_provider_->RemoveObserver(this);
+ for (GCMAppHandlerMap::const_iterator iter = app_handlers_.begin();
+ iter != app_handlers_.end(); ++iter) {
+ iter->second->ShutdownHandler();
+ }
+ app_handlers_.clear();
+ content::BrowserThread::DeleteSoon(content::BrowserThread::IO,
+ FROM_HERE,
+ io_worker_.release());
+}
+
+void GCMService::AddAppHandler(const std::string& app_id,
+ GCMAppHandler* handler) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ DCHECK(!app_id.empty());
+ DCHECK(handler);
+ DCHECK(app_handlers_.find(app_id) == app_handlers_.end());
+
+ app_handlers_[app_id] = handler;
+}
+
+void GCMService::RemoveAppHandler(const std::string& app_id) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ DCHECK(!app_id.empty());
+
+ app_handlers_.erase(app_id);
+}
+
+void GCMService::Register(const std::string& app_id,
+ const std::vector<std::string>& sender_ids,
+ RegisterCallback callback) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ DCHECK(!app_id.empty());
+ DCHECK(!sender_ids.empty());
+ DCHECK(!callback.is_null());
+
+ GCMClient::Result result = EnsureAppReady(app_id);
+ 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(&GCMService::DoRegister,
+ weak_ptr_factory_.GetWeakPtr(),
+ app_id,
+ sender_ids));
+ return;
+ }
+
+ DoRegister(app_id, sender_ids);
+}
+
+void GCMService::DoRegister(const std::string& app_id,
+ const std::vector<std::string>& sender_ids) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ 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());
+
+ content::BrowserThread::PostTask(
+ content::BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&GCMService::IOWorker::Register,
+ base::Unretained(io_worker_.get()),
+ app_id,
+ normalized_sender_ids));
+}
+
+void GCMService::Unregister(const std::string& app_id,
+ UnregisterCallback callback) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ DCHECK(!app_id.empty());
+ DCHECK(!callback.is_null());
+
+ GCMClient::Result result = EnsureAppReady(app_id);
+ 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(&GCMService::DoUnregister,
+ weak_ptr_factory_.GetWeakPtr(),
+ app_id));
+ return;
+ }
+
+ DoUnregister(app_id);
+}
+
+void GCMService::DoUnregister(const std::string& app_id) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+ // 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.
+ content::BrowserThread::PostTask(
+ content::BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&GCMService::IOWorker::Unregister,
+ base::Unretained(io_worker_.get()),
+ app_id));
+}
+
+void GCMService::Send(const std::string& app_id,
+ const std::string& receiver_id,
+ const GCMClient::OutgoingMessage& message,
+ SendCallback callback) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ DCHECK(!app_id.empty());
+ DCHECK(!receiver_id.empty());
+ DCHECK(!callback.is_null());
+
+ GCMClient::Result result = EnsureAppReady(app_id);
+ 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(&GCMService::DoSend,
+ weak_ptr_factory_.GetWeakPtr(),
+ app_id,
+ receiver_id,
+ message));
+ return;
+ }
+
+ DoSend(app_id, receiver_id, message);
+}
+
+void GCMService::DoSend(const std::string& app_id,
+ const std::string& receiver_id,
+ const GCMClient::OutgoingMessage& message) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ content::BrowserThread::PostTask(
+ content::BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&GCMService::IOWorker::Send,
+ base::Unretained(io_worker_.get()),
+ app_id,
+ receiver_id,
+ message));
+}
+
+GCMClient* GCMService::GetGCMClientForTesting() const {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ return io_worker_ ? io_worker_->gcm_client_for_testing() : NULL;
+}
+
+bool GCMService::IsStarted() const {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ return !account_id_.empty();
+}
+
+bool GCMService::IsGCMClientReady() const {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ return gcm_client_ready_;
+}
+
+void GCMService::GetGCMStatistics(GetGCMStatisticsCallback callback,
+ bool clear_logs) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ DCHECK(!callback.is_null());
+
+ request_gcm_statistics_callback_ = callback;
+ content::BrowserThread::PostTask(
+ content::BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&GCMService::IOWorker::GetGCMStatistics,
+ base::Unretained(io_worker_.get()),
+ clear_logs));
+}
+
+void GCMService::SetGCMRecording(GetGCMStatisticsCallback callback,
+ bool recording) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+ request_gcm_statistics_callback_ = callback;
+ content::BrowserThread::PostTask(
+ content::BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&GCMService::IOWorker::SetGCMRecording,
+ base::Unretained(io_worker_.get()),
+ recording));
+}
+
+void GCMService::OnActiveAccountLogin() {
+ if (ShouldStartAutomatically())
+ EnsureLoaded();
+}
+
+void GCMService::OnActiveAccountLogout() {
+ CheckOut();
+}
+
+void GCMService::EnsureLoaded() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ const std::string account_id = identity_provider_->GetActiveAccountId();
+ if (account_id.empty())
+ return;
+
+ // 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;
+ account_id_ = account_id;
+
+ DCHECK(!delayed_task_controller_);
+ delayed_task_controller_.reset(new DelayedTaskController);
+
+ // This will load the data from the gcm store and trigger the check-in if
+ // the persisted check-in info is not found.
+ // Note that we need to pass weak pointer again since the existing weak
+ // pointer in IOWorker might have been invalidated when check-out occurs.
+ content::BrowserThread::PostTask(
+ content::BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&GCMService::IOWorker::Load,
+ base::Unretained(io_worker_.get()),
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void GCMService::RemoveCachedData() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ // 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 GCMService::CheckOut() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+ // 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();
+
+ content::BrowserThread::PostTask(
+ content::BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&GCMService::IOWorker::CheckOut,
+ base::Unretained(io_worker_.get())));
+}
+
+GCMClient::Result GCMService::EnsureAppReady(const std::string& app_id) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ // Ensure that check-in has been done.
+ EnsureLoaded();
+
+ // If the service was not started, bail out.
+ if (account_id_.empty())
+ return GCMClient::NOT_SIGNED_IN;
+
+ return GCMClient::SUCCESS;
+}
+
+bool GCMService::IsAsyncOperationPending(const std::string& app_id) const {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ return register_callbacks_.find(app_id) != register_callbacks_.end() ||
+ unregister_callbacks_.find(app_id) != unregister_callbacks_.end();
+}
+
+void GCMService::RegisterFinished(const std::string& app_id,
+ const std::string& registration_id,
+ GCMClient::Result result) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+ 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 GCMService::UnregisterFinished(const std::string& app_id,
+ GCMClient::Result result) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+ 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 GCMService::SendFinished(const std::string& app_id,
+ const std::string& message_id,
+ GCMClient::Result result) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+ 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 GCMService::MessageReceived(const std::string& app_id,
+ GCMClient::IncomingMessage message) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+ // Drop the event if signed out.
+ if (account_id_.empty())
+ return;
+
+ GetAppHandler(app_id)->OnMessage(app_id, message);
+}
+
+void GCMService::MessagesDeleted(const std::string& app_id) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+ // Drop the event if signed out.
+ if (account_id_.empty())
+ return;
+
+ GetAppHandler(app_id)->OnMessagesDeleted(app_id);
+}
+
+void GCMService::MessageSendError(
+ const std::string& app_id,
+ const GCMClient::SendErrorDetails& send_error_details) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+ // Drop the event if signed out.
+ if (account_id_.empty())
+ return;
+
+ GetAppHandler(app_id)->OnSendError(app_id, send_error_details);
+}
+
+void GCMService::GCMClientReady() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+ if (gcm_client_ready_)
+ return;
+ gcm_client_ready_ = true;
+
+ delayed_task_controller_->SetReady();
+}
+
+GCMAppHandler* GCMService::GetAppHandler(const std::string& app_id) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+ std::map<std::string, GCMAppHandler*>::const_iterator iter =
+ app_handlers_.find(app_id);
+ return iter == app_handlers_.end() ? &default_app_handler_ : iter->second;
+}
+
+void GCMService::GetGCMStatisticsFinished(
+ GCMClient::GCMStatistics stats) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ request_gcm_statistics_callback_.Run(stats);
+}
+
+} // namespace gcm
diff --git a/chrome/browser/services/gcm/gcm_service.h b/chrome/browser/services/gcm/gcm_service.h
new file mode 100644
index 0000000..321be2ea
--- /dev/null
+++ b/chrome/browser/services/gcm/gcm_service.h
@@ -0,0 +1,217 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SERVICES_GCM_GCM_SERVICE_H_
+#define CHROME_BROWSER_SERVICES_GCM_GCM_SERVICE_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/files/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/services/gcm/default_gcm_app_handler.h"
+#include "google_apis/gaia/identity_provider.h"
+#include "google_apis/gcm/gcm_client.h"
+
+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 GCMService : public IdentityProvider::Observer {
+ public:
+ typedef base::Callback<void(const std::string& registration_id,
+ GCMClient::Result result)> RegisterCallback;
+ typedef base::Callback<void(const std::string& message_id,
+ GCMClient::Result result)> SendCallback;
+ typedef base::Callback<void(GCMClient::Result result)> UnregisterCallback;
+ typedef base::Callback<void(const GCMClient::GCMStatistics& stats)>
+ GetGCMStatisticsCallback;
+
+ explicit GCMService(scoped_ptr<IdentityProvider> identity_provider);
+ virtual ~GCMService();
+
+ void Initialize(scoped_ptr<GCMClientFactory> gcm_client_factory);
+
+ void Start();
+
+ void Stop();
+
+ // This method must be called before destroying the GCMService. Once it has
+ // been called, no other GCMService methods may be used.
+ void ShutdownService();
+
+ // Adds a handler for a given app.
+ void AddAppHandler(const std::string& app_id, GCMAppHandler* handler);
+
+ // Remove the handler for a given app.
+ void RemoveAppHandler(const std::string& app_id);
+
+ // Registers |sender_id| for an app. A registration ID will be returned by
+ // the GCM server.
+ // |app_id|: application ID.
+ // |sender_ids|: list of IDs of the servers that are allowed to send the
+ // messages to the application. These IDs are assigned by the
+ // Google API Console.
+ // |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,
+ RegisterCallback callback);
+
+ // 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,
+ UnregisterCallback callback);
+
+ // Sends a message to a given receiver.
+ // |app_id|: application ID.
+ // |receiver_id|: registration ID of the receiver party.
+ // |message|: message to be sent.
+ // |callback|: to be called once the asynchronous operation is done.
+ virtual void Send(const std::string& app_id,
+ const std::string& receiver_id,
+ const GCMClient::OutgoingMessage& message,
+ SendCallback callback);
+
+ // For testing purpose.
+ GCMClient* GetGCMClientForTesting() const;
+
+ // Returns true if the service was started.
+ bool IsStarted() const;
+
+ // Returns true if the gcm client is ready.
+ bool IsGCMClientReady() const;
+
+ // 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(GetGCMStatisticsCallback callback, bool clear_logs);
+
+ // Enables/disables GCM activity recording, and then returns the stats.
+ void SetGCMRecording(GetGCMStatisticsCallback callback, bool recording);
+
+ // IdentityProvider::Observer:
+ virtual void OnActiveAccountLogin() OVERRIDE;
+ virtual void OnActiveAccountLogout() OVERRIDE;
+
+ protected:
+ virtual bool ShouldStartAutomatically() const = 0;
+
+ virtual base::FilePath GetStorePath() const = 0;
+
+ virtual scoped_refptr<net::URLRequestContextGetter>
+ GetURLRequestContextGetter() const = 0;
+
+ scoped_ptr<IdentityProvider> identity_provider_;
+
+ private:
+ friend class TestGCMServiceWrapper;
+ friend class extensions::ExtensionGCMAppHandlerTest;
+
+ class DelayedTaskController;
+ class IOWorker;
+
+ typedef std::map<std::string, GCMAppHandler*> GCMAppHandlerMap;
+
+ // Ensures that the GCMClient is loaded and the GCM check-in is done if the
+ // |identity_provider_| is able to supply an account ID.
+ void EnsureLoaded();
+
+ // Remove cached data when GCM service is stopped.
+ void RemoveCachedData();
+
+ // Checks out of GCM and erases any cached and persisted data.
+ void CheckOut();
+
+ // Ensures that the app is ready for GCM functions and events.
+ GCMClient::Result EnsureAppReady(const std::string& app_id);
+
+ // 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 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<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_;
+
+ // App handler map (from app_id to handler pointer).
+ // The handler is not owned.
+ GCMAppHandlerMap app_handlers_;
+
+ // 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<GCMService> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(GCMService);
+};
+
+} // namespace gcm
+
+#endif // CHROME_BROWSER_SERVICES_GCM_GCM_SERVICE_H_
diff --git a/chrome/browser/services/gcm/gcm_service_unittest.cc b/chrome/browser/services/gcm/gcm_service_unittest.cc
new file mode 100644
index 0000000..b54e8ca
--- /dev/null
+++ b/chrome/browser/services/gcm/gcm_service_unittest.cc
@@ -0,0 +1,1318 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/services/gcm/gcm_service.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/location.h"
+#include "base/run_loop.h"
+#include "base/strings/string_util.h"
+#include "chrome/browser/services/gcm/fake_gcm_client_factory.h"
+#include "chrome/browser/services/gcm/gcm_app_handler.h"
+#include "chrome/browser/services/gcm/gcm_client_factory.h"
+#include "chrome/browser/services/gcm/gcm_client_mock.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "google_apis/gaia/fake_identity_provider.h"
+#include "google_apis/gaia/fake_oauth2_token_service.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "net/url_request/url_request_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gcm {
+
+namespace {
+
+const char kTestAccountID1[] = "user1@example.com";
+const char kTestAccountID2[] = "user2@example.com";
+const char kTestAccountID3[] = "user3@example.com";
+const char kTestAppID1[] = "TestApp1";
+const char kTestAppID2[] = "TestApp2";
+const char kUserID1[] = "user1";
+const char kUserID2[] = "user2";
+
+void PumpCurrentLoop() {
+ base::RunLoop().RunUntilIdle();
+}
+
+void PumpUILoop() {
+ PumpCurrentLoop();
+}
+
+void PumpIOLoop() {
+ base::RunLoop run_loop;
+ content::BrowserThread::PostTaskAndReply(content::BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&PumpCurrentLoop),
+ run_loop.QuitClosure());
+ run_loop.Run();
+}
+
+std::vector<std::string> ToSenderList(const std::string& sender_ids) {
+ std::vector<std::string> senders;
+ Tokenize(sender_ids, ",", &senders);
+ return senders;
+}
+
+class FakeGCMAppHandler : public GCMAppHandler {
+ public:
+ enum Event {
+ NO_EVENT,
+ MESSAGE_EVENT,
+ MESSAGES_DELETED_EVENT,
+ SEND_ERROR_EVENT
+ };
+
+ FakeGCMAppHandler();
+ virtual ~FakeGCMAppHandler();
+
+ const Event& received_event() const { return received_event_; }
+ const std::string& app_id() const { return app_id_; }
+ const GCMClient::IncomingMessage& message() const { return message_; }
+ const GCMClient::SendErrorDetails& send_error_details() const {
+ return send_error_details_;
+ }
+
+ void WaitForNotification();
+
+ // GCMAppHandler:
+ virtual void ShutdownHandler() OVERRIDE;
+ virtual void OnMessage(const std::string& app_id,
+ const GCMClient::IncomingMessage& message) OVERRIDE;
+ virtual void OnMessagesDeleted(const std::string& app_id) OVERRIDE;
+ virtual void OnSendError(
+ const std::string& app_id,
+ const GCMClient::SendErrorDetails& send_error_details) OVERRIDE;
+
+ private:
+ void ClearResults();
+
+ scoped_ptr<base::RunLoop> run_loop_;
+
+ Event received_event_;
+ std::string app_id_;
+ GCMClient::IncomingMessage message_;
+ GCMClient::SendErrorDetails send_error_details_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeGCMAppHandler);
+};
+
+class TestGCMService : public GCMService {
+ public:
+ TestGCMService(
+ bool start_automatically,
+ scoped_ptr<IdentityProvider> identity_provider,
+ const scoped_refptr<net::URLRequestContextGetter>& request_context);
+ virtual ~TestGCMService();
+
+ protected:
+ // GCMService:
+ virtual bool ShouldStartAutomatically() const OVERRIDE;
+ virtual base::FilePath GetStorePath() const OVERRIDE;
+ virtual scoped_refptr<net::URLRequestContextGetter>
+ GetURLRequestContextGetter() const OVERRIDE;
+
+ private:
+ base::ScopedTempDir temp_dir_;
+ scoped_refptr<net::URLRequestContextGetter> request_context_;
+ const bool start_automatically_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestGCMService);
+};
+
+FakeGCMAppHandler::FakeGCMAppHandler() : received_event_(NO_EVENT) {
+}
+
+FakeGCMAppHandler::~FakeGCMAppHandler() {
+}
+
+void FakeGCMAppHandler::WaitForNotification() {
+ run_loop_.reset(new base::RunLoop);
+ run_loop_->Run();
+ run_loop_.reset();
+}
+
+void FakeGCMAppHandler::ShutdownHandler() {
+}
+
+void FakeGCMAppHandler::OnMessage(const std::string& app_id,
+ const GCMClient::IncomingMessage& message) {
+ ClearResults();
+ received_event_ = MESSAGE_EVENT;
+ app_id_ = app_id;
+ message_ = message;
+ if (run_loop_)
+ run_loop_->Quit();
+}
+
+void FakeGCMAppHandler::OnMessagesDeleted(const std::string& app_id) {
+ ClearResults();
+ received_event_ = MESSAGES_DELETED_EVENT;
+ app_id_ = app_id;
+ if (run_loop_)
+ run_loop_->Quit();
+}
+
+void FakeGCMAppHandler::OnSendError(
+ const std::string& app_id,
+ const GCMClient::SendErrorDetails& send_error_details) {
+ ClearResults();
+ received_event_ = SEND_ERROR_EVENT;
+ app_id_ = app_id;
+ send_error_details_ = send_error_details;
+ if (run_loop_)
+ run_loop_->Quit();
+}
+
+void FakeGCMAppHandler::ClearResults() {
+ received_event_ = NO_EVENT;
+ app_id_.clear();
+ message_ = GCMClient::IncomingMessage();
+ send_error_details_ = GCMClient::SendErrorDetails();
+}
+
+TestGCMService::TestGCMService(
+ bool start_automatically,
+ scoped_ptr<IdentityProvider> identity_provider,
+ const scoped_refptr<net::URLRequestContextGetter>& request_context)
+ : GCMService(identity_provider.Pass()),
+ request_context_(request_context),
+ start_automatically_(start_automatically) {
+ if (!temp_dir_.CreateUniqueTempDir())
+ ADD_FAILURE();
+}
+
+TestGCMService::~TestGCMService() {
+}
+
+bool TestGCMService::ShouldStartAutomatically() const {
+ return start_automatically_;
+}
+
+base::FilePath TestGCMService::GetStorePath() const {
+ return temp_dir_.path();
+}
+
+scoped_refptr<net::URLRequestContextGetter>
+TestGCMService::GetURLRequestContextGetter() const {
+ return request_context_;
+}
+
+} // namespace
+
+class TestGCMServiceWrapper {
+ public:
+ enum WaitToFinish {
+ DO_NOT_WAIT,
+ WAIT
+ };
+
+ explicit TestGCMServiceWrapper(
+ const scoped_refptr<net::URLRequestContextGetter>& request_context);
+ ~TestGCMServiceWrapper();
+
+ TestGCMService* service() { return service_.get(); }
+ FakeGCMAppHandler* gcm_app_handler() { return gcm_app_handler_.get(); }
+ const std::string& registration_id() const { return registration_id_; }
+ GCMClient::Result registration_result() const { return registration_result_; }
+ const std::string& send_message_id() const { return send_message_id_; }
+ GCMClient::Result send_result() const { return send_result_; }
+ GCMClient::Result unregistration_result() const {
+ return unregistration_result_;
+ }
+
+ void ClearRegistrationResult();
+ void ClearUnregistrationResult();
+
+ bool ServiceHasAppHandlers() const;
+ GCMClientMock* GetGCMClient();
+
+ void CreateService(bool start_automatically,
+ GCMClientMock::LoadingDelay gcm_client_loading_delay);
+
+ void SignIn(const std::string& account_id);
+ void SignOut();
+
+ void Register(const std::string& app_id,
+ const std::vector<std::string>& sender_ids,
+ WaitToFinish wait_to_finish);
+ void Send(const std::string& app_id,
+ const std::string& receiver_id,
+ const GCMClient::OutgoingMessage& message,
+ WaitToFinish wait_to_finish);
+ void Unregister(const std::string& app_id, WaitToFinish wait_to_finish);
+
+ void WaitForAsyncOperation();
+
+ private:
+ void RegisterCompleted(const std::string& registration_id,
+ GCMClient::Result result);
+ void SendCompleted(const std::string& message_id, GCMClient::Result result);
+ void UnregisterCompleted(GCMClient::Result result);
+
+ scoped_refptr<net::URLRequestContextGetter> request_context_;
+ FakeOAuth2TokenService token_service_;
+ scoped_ptr<FakeIdentityProvider> identity_provider_owner_;
+ FakeIdentityProvider* identity_provider_;
+ scoped_ptr<TestGCMService> service_;
+ scoped_ptr<FakeGCMAppHandler> gcm_app_handler_;
+
+ base::Closure async_operation_completed_callback_;
+
+ std::string registration_id_;
+ GCMClient::Result registration_result_;
+ std::string send_message_id_;
+ GCMClient::Result send_result_;
+ GCMClient::Result unregistration_result_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestGCMServiceWrapper);
+};
+
+TestGCMServiceWrapper::TestGCMServiceWrapper(
+ const scoped_refptr<net::URLRequestContextGetter>& request_context)
+ : request_context_(request_context),
+ identity_provider_(NULL),
+ registration_result_(GCMClient::UNKNOWN_ERROR),
+ send_result_(GCMClient::UNKNOWN_ERROR),
+ unregistration_result_(GCMClient::UNKNOWN_ERROR) {
+ identity_provider_owner_.reset(new FakeIdentityProvider(&token_service_));
+ identity_provider_ = identity_provider_owner_.get();
+}
+
+TestGCMServiceWrapper::~TestGCMServiceWrapper() {
+ if (!service_)
+ return;
+
+ service_->ShutdownService();
+ service_.reset();
+ PumpIOLoop();
+}
+
+void TestGCMServiceWrapper::ClearRegistrationResult() {
+ registration_id_.clear();
+ registration_result_ = GCMClient::UNKNOWN_ERROR;
+}
+
+void TestGCMServiceWrapper::ClearUnregistrationResult() {
+ unregistration_result_ = GCMClient::UNKNOWN_ERROR;
+}
+
+bool TestGCMServiceWrapper::ServiceHasAppHandlers() const {
+ return !service_->app_handlers_.empty();
+}
+
+GCMClientMock* TestGCMServiceWrapper::GetGCMClient() {
+ return static_cast<GCMClientMock*>(service_->GetGCMClientForTesting());
+}
+
+void TestGCMServiceWrapper::CreateService(
+ bool start_automatically,
+ GCMClientMock::LoadingDelay gcm_client_loading_delay) {
+ service_.reset(new TestGCMService(
+ start_automatically,
+ identity_provider_owner_.PassAs<IdentityProvider>(),
+ request_context_));
+ service_->Initialize(scoped_ptr<GCMClientFactory>(
+ new FakeGCMClientFactory(gcm_client_loading_delay)));
+
+ gcm_app_handler_.reset(new FakeGCMAppHandler);
+ service_->AddAppHandler(kTestAppID1, gcm_app_handler_.get());
+ service_->AddAppHandler(kTestAppID2, gcm_app_handler_.get());
+}
+
+void TestGCMServiceWrapper::SignIn(const std::string& account_id) {
+ token_service_.AddAccount(account_id);
+ identity_provider_->LogIn(account_id);
+ PumpIOLoop();
+ PumpUILoop();
+}
+
+void TestGCMServiceWrapper::SignOut() {
+ identity_provider_->LogOut();
+ PumpIOLoop();
+ PumpUILoop();
+}
+
+void TestGCMServiceWrapper::Register(const std::string& app_id,
+ const std::vector<std::string>& sender_ids,
+ WaitToFinish wait_to_finish) {
+ base::RunLoop run_loop;
+ async_operation_completed_callback_ = run_loop.QuitClosure();
+ service_->Register(app_id,
+ sender_ids,
+ base::Bind(&TestGCMServiceWrapper::RegisterCompleted,
+ base::Unretained(this)));
+ if (wait_to_finish == WAIT)
+ run_loop.Run();
+}
+
+void TestGCMServiceWrapper::Send(const std::string& app_id,
+ const std::string& receiver_id,
+ const GCMClient::OutgoingMessage& message,
+ WaitToFinish wait_to_finish) {
+ base::RunLoop run_loop;
+ async_operation_completed_callback_ = run_loop.QuitClosure();
+ service_->Send(app_id,
+ receiver_id,
+ message,
+ base::Bind(&TestGCMServiceWrapper::SendCompleted,
+ base::Unretained(this)));
+ if (wait_to_finish == WAIT)
+ run_loop.Run();
+}
+
+void TestGCMServiceWrapper::Unregister(const std::string& app_id,
+ WaitToFinish wait_to_finish) {
+ base::RunLoop run_loop;
+ async_operation_completed_callback_ = run_loop.QuitClosure();
+ service_->Unregister(app_id,
+ base::Bind(&TestGCMServiceWrapper::UnregisterCompleted,
+ base::Unretained(this)));
+ if (wait_to_finish == WAIT)
+ run_loop.Run();
+}
+
+void TestGCMServiceWrapper::WaitForAsyncOperation() {
+ base::RunLoop run_loop;
+ async_operation_completed_callback_ = run_loop.QuitClosure();
+ run_loop.Run();
+}
+
+void TestGCMServiceWrapper::RegisterCompleted(
+ const std::string& registration_id,
+ GCMClient::Result result) {
+ registration_id_ = registration_id;
+ registration_result_ = result;
+ if (!async_operation_completed_callback_.is_null())
+ async_operation_completed_callback_.Run();
+}
+
+void TestGCMServiceWrapper::SendCompleted(const std::string& message_id,
+ GCMClient::Result result) {
+ send_message_id_ = message_id;
+ send_result_ = result;
+ if (!async_operation_completed_callback_.is_null())
+ async_operation_completed_callback_.Run();
+}
+
+void TestGCMServiceWrapper::UnregisterCompleted(GCMClient::Result result) {
+ unregistration_result_ = result;
+ if (!async_operation_completed_callback_.is_null())
+ async_operation_completed_callback_.Run();
+}
+
+class GCMServiceTest : public testing::Test {
+ protected:
+ GCMServiceTest();
+ virtual ~GCMServiceTest();
+
+ // testing::Test:
+ virtual void SetUp() OVERRIDE;
+ virtual void TearDown() OVERRIDE;
+
+ scoped_ptr<content::TestBrowserThreadBundle> thread_bundle_;
+ scoped_refptr<net::URLRequestContextGetter> request_context_;
+ scoped_ptr<TestGCMServiceWrapper> wrapper_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(GCMServiceTest);
+};
+
+GCMServiceTest::GCMServiceTest() {
+}
+
+GCMServiceTest::~GCMServiceTest() {
+}
+
+void GCMServiceTest::SetUp() {
+ thread_bundle_.reset(new content::TestBrowserThreadBundle(
+ content::TestBrowserThreadBundle::REAL_IO_THREAD));
+ request_context_ = new net::TestURLRequestContextGetter(
+ content::BrowserThread::GetMessageLoopProxyForThread(
+ content::BrowserThread::IO));
+ wrapper_.reset(new TestGCMServiceWrapper(request_context_));
+}
+
+void GCMServiceTest::TearDown() {
+ wrapper_.reset();
+}
+
+TEST_F(GCMServiceTest, CreateGCMServiceBeforeSignIn) {
+ // Create CreateGMCService first.
+ wrapper_->CreateService(true, GCMClientMock::NO_DELAY_LOADING);
+ EXPECT_FALSE(wrapper_->service()->IsStarted());
+
+ // Sign in. This will kick off the check-in.
+ wrapper_->SignIn(kTestAccountID1);
+ EXPECT_TRUE(wrapper_->service()->IsStarted());
+}
+
+TEST_F(GCMServiceTest, CreateGCMServiceAfterSignIn) {
+ // Sign in. This will not initiate the check-in.
+ wrapper_->SignIn(kTestAccountID1);
+
+ // Create GCMeService after sign-in.
+ wrapper_->CreateService(true, GCMClientMock::NO_DELAY_LOADING);
+ EXPECT_TRUE(wrapper_->service()->IsStarted());
+}
+
+TEST_F(GCMServiceTest, Shutdown) {
+ wrapper_->CreateService(true, GCMClientMock::NO_DELAY_LOADING);
+ EXPECT_TRUE(wrapper_->ServiceHasAppHandlers());
+
+ wrapper_->service()->ShutdownService();
+ EXPECT_FALSE(wrapper_->ServiceHasAppHandlers());
+}
+
+TEST_F(GCMServiceTest, SignInAndSignOutUnderPositiveChannelSignal) {
+ wrapper_->CreateService(true, GCMClientMock::NO_DELAY_LOADING);
+ wrapper_->SignIn(kTestAccountID1);
+
+ // GCMClient should be loaded.
+ EXPECT_TRUE(wrapper_->service()->IsGCMClientReady());
+ EXPECT_EQ(GCMClientMock::LOADED, wrapper_->GetGCMClient()->status());
+
+ wrapper_->SignOut();
+
+ // GCMClient should be checked out.
+ EXPECT_FALSE(wrapper_->service()->IsGCMClientReady());
+ EXPECT_EQ(GCMClientMock::CHECKED_OUT, wrapper_->GetGCMClient()->status());
+}
+
+TEST_F(GCMServiceTest, SignInAndSignOutUnderNonPositiveChannelSignal) {
+ // Non-positive channel signal will prevent GCMClient from checking in during
+ // sign-in.
+ wrapper_->CreateService(false, GCMClientMock::NO_DELAY_LOADING);
+ wrapper_->SignIn(kTestAccountID1);
+
+ // GCMClient should not be loaded.
+ EXPECT_FALSE(wrapper_->service()->IsGCMClientReady());
+ EXPECT_EQ(GCMClientMock::UNINITIALIZED, wrapper_->GetGCMClient()->status());
+
+ wrapper_->SignOut();
+
+ // Check-out should still be performed.
+ EXPECT_FALSE(wrapper_->service()->IsGCMClientReady());
+ EXPECT_EQ(GCMClientMock::CHECKED_OUT, wrapper_->GetGCMClient()->status());
+}
+
+TEST_F(GCMServiceTest, SignOutAndThenSignIn) {
+ wrapper_->CreateService(true, GCMClientMock::NO_DELAY_LOADING);
+ wrapper_->SignIn(kTestAccountID1);
+
+ // GCMClient should be loaded.
+ EXPECT_TRUE(wrapper_->service()->IsGCMClientReady());
+ EXPECT_EQ(GCMClientMock::LOADED, wrapper_->GetGCMClient()->status());
+
+ wrapper_->SignOut();
+
+ // GCMClient should be checked out.
+ EXPECT_FALSE(wrapper_->service()->IsGCMClientReady());
+ EXPECT_EQ(GCMClientMock::CHECKED_OUT, wrapper_->GetGCMClient()->status());
+
+ // Sign-in with a different account.
+ wrapper_->SignIn(kTestAccountID2);
+
+ // GCMClient should be loaded again.
+ EXPECT_TRUE(wrapper_->service()->IsGCMClientReady());
+ EXPECT_EQ(GCMClientMock::LOADED, wrapper_->GetGCMClient()->status());
+}
+
+TEST_F(GCMServiceTest, StopAndRestartGCM) {
+ wrapper_->CreateService(true, GCMClientMock::NO_DELAY_LOADING);
+ wrapper_->SignIn(kTestAccountID1);
+
+ // GCMClient should be loaded.
+ EXPECT_TRUE(wrapper_->service()->IsGCMClientReady());
+ EXPECT_EQ(GCMClientMock::LOADED, wrapper_->GetGCMClient()->status());
+
+ // Stops the GCM.
+ wrapper_->service()->Stop();
+ PumpIOLoop();
+ PumpUILoop();
+
+ // GCMClient should be stopped.
+ EXPECT_FALSE(wrapper_->service()->IsGCMClientReady());
+ EXPECT_EQ(GCMClientMock::STOPPED, wrapper_->GetGCMClient()->status());
+
+ // Restarts the GCM.
+ wrapper_->service()->Start();
+ PumpIOLoop();
+ PumpUILoop();
+
+ // GCMClient should be loaded.
+ EXPECT_TRUE(wrapper_->service()->IsGCMClientReady());
+ EXPECT_EQ(GCMClientMock::LOADED, wrapper_->GetGCMClient()->status());
+
+ // Stops the GCM.
+ wrapper_->service()->Stop();
+ PumpIOLoop();
+ PumpUILoop();
+
+ // GCMClient should be stopped.
+ EXPECT_FALSE(wrapper_->service()->IsGCMClientReady());
+ EXPECT_EQ(GCMClientMock::STOPPED, wrapper_->GetGCMClient()->status());
+
+ // Sign out.
+ wrapper_->SignOut();
+
+ // GCMClient should be checked out.
+ EXPECT_FALSE(wrapper_->service()->IsGCMClientReady());
+ EXPECT_EQ(GCMClientMock::CHECKED_OUT, wrapper_->GetGCMClient()->status());
+}
+
+TEST_F(GCMServiceTest, RegisterWhenNotSignedIn) {
+ wrapper_->CreateService(true, GCMClientMock::NO_DELAY_LOADING);
+
+ std::vector<std::string> sender_ids;
+ sender_ids.push_back("sender1");
+ wrapper_->Register(kTestAppID1, sender_ids, TestGCMServiceWrapper::WAIT);
+
+ EXPECT_TRUE(wrapper_->registration_id().empty());
+ EXPECT_EQ(GCMClient::NOT_SIGNED_IN, wrapper_->registration_result());
+}
+
+TEST_F(GCMServiceTest, RegisterUnderNonPositiveChannelSignal) {
+ // Non-positive channel signal will prevent GCMClient from checking in during
+ // sign-in.
+ wrapper_->CreateService(false, GCMClientMock::NO_DELAY_LOADING);
+ wrapper_->SignIn(kTestAccountID1);
+
+ // GCMClient should not be checked in.
+ EXPECT_FALSE(wrapper_->service()->IsGCMClientReady());
+ EXPECT_EQ(GCMClientMock::UNINITIALIZED, wrapper_->GetGCMClient()->status());
+
+ // Invoking register will make GCMClient checked in.
+ std::vector<std::string> sender_ids;
+ sender_ids.push_back("sender1");
+ wrapper_->Register(kTestAppID1, sender_ids, TestGCMServiceWrapper::WAIT);
+
+ // GCMClient should be checked in.
+ EXPECT_TRUE(wrapper_->service()->IsGCMClientReady());
+ EXPECT_EQ(GCMClientMock::LOADED, wrapper_->GetGCMClient()->status());
+
+ // Registration should succeed.
+ const std::string expected_registration_id =
+ GCMClientMock::GetRegistrationIdFromSenderIds(sender_ids);
+ EXPECT_EQ(expected_registration_id, wrapper_->registration_id());
+ EXPECT_EQ(GCMClient::SUCCESS, wrapper_->registration_result());
+}
+
+TEST_F(GCMServiceTest, SendWhenNotSignedIn) {
+ wrapper_->CreateService(true, GCMClientMock::NO_DELAY_LOADING);
+
+ GCMClient::OutgoingMessage message;
+ message.id = "1";
+ message.data["key1"] = "value1";
+ wrapper_->Send(kTestAppID1, kUserID1, message, TestGCMServiceWrapper::WAIT);
+
+ EXPECT_TRUE(wrapper_->send_message_id().empty());
+ EXPECT_EQ(GCMClient::NOT_SIGNED_IN, wrapper_->send_result());
+}
+
+TEST_F(GCMServiceTest, SendUnderNonPositiveChannelSignal) {
+ // Non-positive channel signal will prevent GCMClient from checking in during
+ // sign-in.
+ wrapper_->CreateService(false, GCMClientMock::NO_DELAY_LOADING);
+ wrapper_->SignIn(kTestAccountID1);
+
+ // GCMClient should not be checked in.
+ EXPECT_FALSE(wrapper_->service()->IsGCMClientReady());
+ EXPECT_EQ(GCMClientMock::UNINITIALIZED, wrapper_->GetGCMClient()->status());
+
+ // Invoking send will make GCMClient checked in.
+ GCMClient::OutgoingMessage message;
+ message.id = "1";
+ message.data["key1"] = "value1";
+ wrapper_->Send(kTestAppID1, kUserID1, message, TestGCMServiceWrapper::WAIT);
+
+ // GCMClient should be checked in.
+ EXPECT_TRUE(wrapper_->service()->IsGCMClientReady());
+ EXPECT_EQ(GCMClientMock::LOADED, wrapper_->GetGCMClient()->status());
+
+ // Sending should succeed.
+ EXPECT_EQ(message.id, wrapper_->send_message_id());
+ EXPECT_EQ(GCMClient::SUCCESS, wrapper_->send_result());
+}
+
+// Tests a single instance of GCMService.
+class GCMServiceSingleInstanceTest : public GCMServiceTest {
+ public:
+ GCMServiceSingleInstanceTest();
+ virtual ~GCMServiceSingleInstanceTest();
+
+ // GCMServiceTest:
+ virtual void SetUp() OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(GCMServiceSingleInstanceTest);
+};
+
+GCMServiceSingleInstanceTest::GCMServiceSingleInstanceTest() {
+}
+
+GCMServiceSingleInstanceTest::~GCMServiceSingleInstanceTest() {
+}
+
+void GCMServiceSingleInstanceTest::SetUp() {
+ GCMServiceTest::SetUp();
+
+ wrapper_->CreateService(true, GCMClientMock::NO_DELAY_LOADING);
+ wrapper_->SignIn(kTestAccountID1);
+}
+
+TEST_F(GCMServiceSingleInstanceTest, Register) {
+ std::vector<std::string> sender_ids;
+ sender_ids.push_back("sender1");
+ wrapper_->Register(kTestAppID1, sender_ids, TestGCMServiceWrapper::WAIT);
+ const std::string expected_registration_id =
+ GCMClientMock::GetRegistrationIdFromSenderIds(sender_ids);
+
+ EXPECT_EQ(expected_registration_id, wrapper_->registration_id());
+ EXPECT_EQ(GCMClient::SUCCESS, wrapper_->registration_result());
+}
+
+TEST_F(GCMServiceSingleInstanceTest, RegisterError) {
+ std::vector<std::string> sender_ids;
+ sender_ids.push_back("sender1@error");
+ wrapper_->Register(kTestAppID1, sender_ids, TestGCMServiceWrapper::WAIT);
+
+ EXPECT_TRUE(wrapper_->registration_id().empty());
+ EXPECT_NE(GCMClient::SUCCESS, wrapper_->registration_result());
+}
+
+TEST_F(GCMServiceSingleInstanceTest, RegisterAgainWithSameSenderIDs) {
+ std::vector<std::string> sender_ids;
+ sender_ids.push_back("sender1");
+ sender_ids.push_back("sender2");
+ wrapper_->Register(kTestAppID1, sender_ids, TestGCMServiceWrapper::WAIT);
+ const std::string expected_registration_id =
+ GCMClientMock::GetRegistrationIdFromSenderIds(sender_ids);
+
+ EXPECT_EQ(expected_registration_id, wrapper_->registration_id());
+ EXPECT_EQ(GCMClient::SUCCESS, wrapper_->registration_result());
+
+ // Clears the results the would be set by the Register callback in preparation
+ // to call register 2nd time.
+ wrapper_->ClearRegistrationResult();
+
+ // Calling register 2nd time with the same set of sender IDs but different
+ // ordering will get back the same registration ID.
+ std::vector<std::string> another_sender_ids;
+ another_sender_ids.push_back("sender2");
+ another_sender_ids.push_back("sender1");
+ wrapper_->Register(kTestAppID1,
+ another_sender_ids,
+ TestGCMServiceWrapper::WAIT);
+
+ EXPECT_EQ(expected_registration_id, wrapper_->registration_id());
+ EXPECT_EQ(GCMClient::SUCCESS, wrapper_->registration_result());
+}
+
+TEST_F(GCMServiceSingleInstanceTest, RegisterAgainWithDifferentSenderIDs) {
+ std::vector<std::string> sender_ids;
+ sender_ids.push_back("sender1");
+ wrapper_->Register(kTestAppID1, sender_ids, TestGCMServiceWrapper::WAIT);
+ const std::string expected_registration_id =
+ GCMClientMock::GetRegistrationIdFromSenderIds(sender_ids);
+
+ EXPECT_EQ(expected_registration_id, wrapper_->registration_id());
+ EXPECT_EQ(GCMClient::SUCCESS, wrapper_->registration_result());
+
+ // Make sender IDs different.
+ sender_ids.push_back("sender2");
+ const std::string expected_registration_id2 =
+ GCMClientMock::GetRegistrationIdFromSenderIds(sender_ids);
+
+ // Calling register 2nd time with the different sender IDs will get back a new
+ // registration ID.
+ wrapper_->Register(kTestAppID1, sender_ids, TestGCMServiceWrapper::WAIT);
+ EXPECT_EQ(expected_registration_id2, wrapper_->registration_id());
+ EXPECT_EQ(GCMClient::SUCCESS, wrapper_->registration_result());
+}
+
+TEST_F(GCMServiceSingleInstanceTest, GCMClientNotReadyBeforeRegistration) {
+ // Make GCMClient not ready initially.
+ wrapper_.reset(new TestGCMServiceWrapper(request_context_));
+ wrapper_->CreateService(true, GCMClientMock::DELAY_LOADING);
+ wrapper_->SignIn(kTestAccountID1);
+
+ // The registration is on hold until GCMClient is ready.
+ std::vector<std::string> sender_ids;
+ sender_ids.push_back("sender1");
+ wrapper_->Register(kTestAppID1,
+ sender_ids,
+ TestGCMServiceWrapper::DO_NOT_WAIT);
+ PumpIOLoop();
+ PumpUILoop();
+ EXPECT_TRUE(wrapper_->registration_id().empty());
+ EXPECT_EQ(GCMClient::UNKNOWN_ERROR, wrapper_->registration_result());
+
+ // Register operation will be invoked after GCMClient becomes ready.
+ wrapper_->GetGCMClient()->PerformDelayedLoading();
+ wrapper_->WaitForAsyncOperation();
+ EXPECT_FALSE(wrapper_->registration_id().empty());
+ EXPECT_EQ(GCMClient::SUCCESS, wrapper_->registration_result());
+}
+
+TEST_F(GCMServiceSingleInstanceTest, RegisterAfterSignOut) {
+ // This will trigger check-out.
+ wrapper_->SignOut();
+
+ std::vector<std::string> sender_ids;
+ sender_ids.push_back("sender1");
+ wrapper_->Register(kTestAppID1, sender_ids, TestGCMServiceWrapper::WAIT);
+
+ EXPECT_TRUE(wrapper_->registration_id().empty());
+ EXPECT_EQ(GCMClient::NOT_SIGNED_IN, wrapper_->registration_result());
+}
+
+TEST_F(GCMServiceSingleInstanceTest, UnregisterExplicitly) {
+ std::vector<std::string> sender_ids;
+ sender_ids.push_back("sender1");
+ wrapper_->Register(kTestAppID1, sender_ids, TestGCMServiceWrapper::WAIT);
+
+ EXPECT_FALSE(wrapper_->registration_id().empty());
+ EXPECT_EQ(GCMClient::SUCCESS, wrapper_->registration_result());
+
+ wrapper_->Unregister(kTestAppID1, TestGCMServiceWrapper::WAIT);
+
+ EXPECT_EQ(GCMClient::SUCCESS, wrapper_->unregistration_result());
+}
+
+TEST_F(GCMServiceSingleInstanceTest, UnregisterWhenAsyncOperationPending) {
+ std::vector<std::string> sender_ids;
+ sender_ids.push_back("sender1");
+ // First start registration without waiting for it to complete.
+ wrapper_->Register(kTestAppID1,
+ sender_ids,
+ TestGCMServiceWrapper::DO_NOT_WAIT);
+
+ // Test that unregistration fails with async operation pending when there is a
+ // registration already in progress.
+ wrapper_->Unregister(kTestAppID1, TestGCMServiceWrapper::WAIT);
+ EXPECT_EQ(GCMClient::ASYNC_OPERATION_PENDING,
+ wrapper_->unregistration_result());
+
+ // Complete the unregistration.
+ wrapper_->WaitForAsyncOperation();
+ EXPECT_EQ(GCMClient::SUCCESS, wrapper_->registration_result());
+
+ // Start unregistration without waiting for it to complete. This time no async
+ // operation is pending.
+ wrapper_->Unregister(kTestAppID1, TestGCMServiceWrapper::DO_NOT_WAIT);
+
+ // Test that unregistration fails with async operation pending when there is
+ // an unregistration already in progress.
+ wrapper_->Unregister(kTestAppID1, TestGCMServiceWrapper::WAIT);
+ EXPECT_EQ(GCMClient::ASYNC_OPERATION_PENDING,
+ wrapper_->unregistration_result());
+ wrapper_->ClearUnregistrationResult();
+
+ // Complete unregistration.
+ wrapper_->WaitForAsyncOperation();
+ EXPECT_EQ(GCMClient::SUCCESS, wrapper_->unregistration_result());
+}
+
+TEST_F(GCMServiceSingleInstanceTest, RegisterWhenAsyncOperationPending) {
+ std::vector<std::string> sender_ids;
+ sender_ids.push_back("sender1");
+ // First start registration without waiting for it to complete.
+ wrapper_->Register(kTestAppID1,
+ sender_ids,
+ TestGCMServiceWrapper::DO_NOT_WAIT);
+
+ // Test that registration fails with async operation pending when there is a
+ // registration already in progress.
+ wrapper_->Register(kTestAppID1, sender_ids, TestGCMServiceWrapper::WAIT);
+ EXPECT_EQ(GCMClient::ASYNC_OPERATION_PENDING,
+ wrapper_->registration_result());
+ wrapper_->ClearRegistrationResult();
+
+ // Complete the registration.
+ wrapper_->WaitForAsyncOperation();
+ EXPECT_EQ(GCMClient::SUCCESS, wrapper_->registration_result());
+
+ // Start unregistration without waiting for it to complete. This time no async
+ // operation is pending.
+ wrapper_->Unregister(kTestAppID1, TestGCMServiceWrapper::DO_NOT_WAIT);
+
+ // Test that registration fails with async operation pending when there is an
+ // unregistration already in progress.
+ wrapper_->Register(kTestAppID1, sender_ids, TestGCMServiceWrapper::WAIT);
+ EXPECT_EQ(GCMClient::ASYNC_OPERATION_PENDING,
+ wrapper_->registration_result());
+
+ // Complete the first unregistration expecting success.
+ wrapper_->WaitForAsyncOperation();
+ EXPECT_EQ(GCMClient::SUCCESS, wrapper_->unregistration_result());
+
+ // Test that it is ok to register again after unregistration.
+ wrapper_->Register(kTestAppID1, sender_ids, TestGCMServiceWrapper::WAIT);
+ EXPECT_EQ(GCMClient::SUCCESS, wrapper_->registration_result());
+}
+
+TEST_F(GCMServiceSingleInstanceTest, Send) {
+ GCMClient::OutgoingMessage message;
+ message.id = "1";
+ message.data["key1"] = "value1";
+ message.data["key2"] = "value2";
+ wrapper_->Send(kTestAppID1, kUserID1, message, TestGCMServiceWrapper::WAIT);
+
+ EXPECT_EQ(message.id, wrapper_->send_message_id());
+ EXPECT_EQ(GCMClient::SUCCESS, wrapper_->send_result());
+}
+
+TEST_F(GCMServiceSingleInstanceTest, GCMClientNotReadyBeforeSending) {
+ // Make GCMClient not ready initially.
+ wrapper_.reset(new TestGCMServiceWrapper(request_context_));
+ wrapper_->CreateService(true, GCMClientMock::DELAY_LOADING);
+ wrapper_->SignIn(kTestAccountID1);
+
+ // The sending is on hold until GCMClient is ready.
+ GCMClient::OutgoingMessage message;
+ message.id = "1";
+ message.data["key1"] = "value1";
+ message.data["key2"] = "value2";
+ wrapper_->Send(kTestAppID1,
+ kUserID1,
+ message,
+ TestGCMServiceWrapper::DO_NOT_WAIT);
+ PumpIOLoop();
+ PumpUILoop();
+
+ EXPECT_TRUE(wrapper_->send_message_id().empty());
+ EXPECT_EQ(GCMClient::UNKNOWN_ERROR, wrapper_->send_result());
+
+ // Send operation will be invoked after GCMClient becomes ready.
+ wrapper_->GetGCMClient()->PerformDelayedLoading();
+ wrapper_->WaitForAsyncOperation();
+ EXPECT_EQ(message.id, wrapper_->send_message_id());
+ EXPECT_EQ(GCMClient::SUCCESS, wrapper_->send_result());
+}
+
+TEST_F(GCMServiceSingleInstanceTest, SendAfterSignOut) {
+ // This will trigger check-out.
+ wrapper_->SignOut();
+
+ GCMClient::OutgoingMessage message;
+ message.id = "1";
+ message.data["key1"] = "value1";
+ message.data["key2"] = "value2";
+ wrapper_->Send(kTestAppID1, kUserID1, message, TestGCMServiceWrapper::WAIT);
+
+ EXPECT_TRUE(wrapper_->send_message_id().empty());
+ EXPECT_EQ(GCMClient::NOT_SIGNED_IN, wrapper_->send_result());
+}
+
+TEST_F(GCMServiceSingleInstanceTest, SendError) {
+ GCMClient::OutgoingMessage message;
+ // Embedding error in id will tell the mock to simulate the send error.
+ message.id = "1@error";
+ message.data["key1"] = "value1";
+ message.data["key2"] = "value2";
+ wrapper_->Send(kTestAppID1, kUserID1, message, TestGCMServiceWrapper::WAIT);
+
+ EXPECT_EQ(message.id, wrapper_->send_message_id());
+ EXPECT_EQ(GCMClient::SUCCESS, wrapper_->send_result());
+
+ // Wait for the send error.
+ wrapper_->gcm_app_handler()->WaitForNotification();
+ EXPECT_EQ(FakeGCMAppHandler::SEND_ERROR_EVENT,
+ wrapper_->gcm_app_handler()->received_event());
+ EXPECT_EQ(kTestAppID1, wrapper_->gcm_app_handler()->app_id());
+ EXPECT_EQ(message.id,
+ wrapper_->gcm_app_handler()->send_error_details().message_id);
+ EXPECT_NE(GCMClient::SUCCESS,
+ wrapper_->gcm_app_handler()->send_error_details().result);
+ EXPECT_EQ(message.data,
+ wrapper_->gcm_app_handler()->send_error_details().additional_data);
+}
+
+TEST_F(GCMServiceSingleInstanceTest, MessageReceived) {
+ wrapper_->Register(kTestAppID1,
+ ToSenderList("sender"),
+ TestGCMServiceWrapper::WAIT);
+ GCMClient::IncomingMessage message;
+ message.data["key1"] = "value1";
+ message.data["key2"] = "value2";
+ message.sender_id = "sender";
+ wrapper_->GetGCMClient()->ReceiveMessage(kTestAppID1, message);
+ wrapper_->gcm_app_handler()->WaitForNotification();
+ EXPECT_EQ(FakeGCMAppHandler::MESSAGE_EVENT,
+ wrapper_->gcm_app_handler()->received_event());
+ EXPECT_EQ(kTestAppID1, wrapper_->gcm_app_handler()->app_id());
+ EXPECT_EQ(message.data, wrapper_->gcm_app_handler()->message().data);
+ EXPECT_TRUE(wrapper_->gcm_app_handler()->message().collapse_key.empty());
+ EXPECT_EQ(message.sender_id,
+ wrapper_->gcm_app_handler()->message().sender_id);
+}
+
+TEST_F(GCMServiceSingleInstanceTest, MessageWithCollapseKeyReceived) {
+ wrapper_->Register(kTestAppID1,
+ ToSenderList("sender"),
+ TestGCMServiceWrapper::WAIT);
+ GCMClient::IncomingMessage message;
+ message.data["key1"] = "value1";
+ message.collapse_key = "collapse_key_value";
+ message.sender_id = "sender";
+ wrapper_->GetGCMClient()->ReceiveMessage(kTestAppID1, message);
+ wrapper_->gcm_app_handler()->WaitForNotification();
+ EXPECT_EQ(FakeGCMAppHandler::MESSAGE_EVENT,
+ wrapper_->gcm_app_handler()->received_event());
+ EXPECT_EQ(kTestAppID1, wrapper_->gcm_app_handler()->app_id());
+ EXPECT_EQ(message.data, wrapper_->gcm_app_handler()->message().data);
+ EXPECT_EQ(message.collapse_key,
+ wrapper_->gcm_app_handler()->message().collapse_key);
+}
+
+TEST_F(GCMServiceSingleInstanceTest, MessagesDeleted) {
+ wrapper_->GetGCMClient()->DeleteMessages(kTestAppID1);
+ wrapper_->gcm_app_handler()->WaitForNotification();
+ EXPECT_EQ(FakeGCMAppHandler::MESSAGES_DELETED_EVENT,
+ wrapper_->gcm_app_handler()->received_event());
+ EXPECT_EQ(kTestAppID1, wrapper_->gcm_app_handler()->app_id());
+}
+
+// Tests to make sure that concurrent GCMService instances work correctly
+// regardless how GCMClient is created.
+class GCMServiceMultipleInstanceTest : public GCMServiceTest {
+ protected:
+ GCMServiceMultipleInstanceTest();
+ virtual ~GCMServiceMultipleInstanceTest();
+
+ // GCMServiceTest:
+ virtual void SetUp() OVERRIDE;
+ virtual void TearDown() OVERRIDE;
+
+ scoped_ptr<TestGCMServiceWrapper> wrapper2_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(GCMServiceMultipleInstanceTest);
+};
+
+GCMServiceMultipleInstanceTest::GCMServiceMultipleInstanceTest() {
+}
+
+GCMServiceMultipleInstanceTest::~GCMServiceMultipleInstanceTest() {
+}
+
+void GCMServiceMultipleInstanceTest::SetUp() {
+ GCMServiceTest::SetUp();
+
+ wrapper2_.reset(new TestGCMServiceWrapper(request_context_));
+
+ wrapper_->CreateService(true, GCMClientMock::NO_DELAY_LOADING);
+ wrapper2_->CreateService(true, GCMClientMock::NO_DELAY_LOADING);
+
+ // Initiate check-in for each instance.
+ wrapper_->SignIn(kTestAccountID1);
+ wrapper2_->SignIn(kTestAccountID2);
+}
+
+void GCMServiceMultipleInstanceTest::TearDown() {
+ wrapper2_.reset();
+}
+
+TEST_F(GCMServiceMultipleInstanceTest, Register) {
+ // Register an app.
+ std::vector<std::string> sender_ids;
+ sender_ids.push_back("sender1");
+ wrapper_->Register(kTestAppID1, sender_ids, TestGCMServiceWrapper::WAIT);
+
+ // Register the same app in a different instance.
+ std::vector<std::string> sender_ids2;
+ sender_ids2.push_back("foo");
+ sender_ids2.push_back("bar");
+ wrapper2_->Register(kTestAppID1, sender_ids2, TestGCMServiceWrapper::WAIT);
+
+ EXPECT_EQ(GCMClientMock::GetRegistrationIdFromSenderIds(sender_ids),
+ wrapper_->registration_id());
+ EXPECT_EQ(GCMClient::SUCCESS, wrapper_->registration_result());
+
+ EXPECT_EQ(GCMClientMock::GetRegistrationIdFromSenderIds(sender_ids2),
+ wrapper2_->registration_id());
+ EXPECT_EQ(GCMClient::SUCCESS, wrapper2_->registration_result());
+
+ // Register a different app in a different instance.
+ std::vector<std::string> sender_ids3;
+ sender_ids3.push_back("sender1");
+ sender_ids3.push_back("sender2");
+ sender_ids3.push_back("sender3");
+ wrapper2_->Register(kTestAppID2, sender_ids3, TestGCMServiceWrapper::WAIT);
+
+ EXPECT_EQ(GCMClientMock::GetRegistrationIdFromSenderIds(sender_ids3),
+ wrapper2_->registration_id());
+ EXPECT_EQ(GCMClient::SUCCESS, wrapper2_->registration_result());
+}
+
+TEST_F(GCMServiceMultipleInstanceTest, Send) {
+ // Send a message from one app in one instance.
+ GCMClient::OutgoingMessage message;
+ message.id = "1";
+ message.data["key1"] = "value1";
+ message.data["key2"] = "value2";
+ wrapper_->Send(kTestAppID1, kUserID1, message, TestGCMServiceWrapper::WAIT);
+
+ // Send a message from same app in another instance.
+ GCMClient::OutgoingMessage message2;
+ message2.id = "2";
+ message2.data["foo"] = "bar";
+ wrapper2_->Send(kTestAppID1, kUserID2, message2, TestGCMServiceWrapper::WAIT);
+
+ EXPECT_EQ(message.id, wrapper_->send_message_id());
+ EXPECT_EQ(GCMClient::SUCCESS, wrapper_->send_result());
+
+ EXPECT_EQ(message2.id, wrapper2_->send_message_id());
+ EXPECT_EQ(GCMClient::SUCCESS, wrapper2_->send_result());
+
+ // Send another message from different app in another instance.
+ GCMClient::OutgoingMessage message3;
+ message3.id = "3";
+ message3.data["hello"] = "world";
+ wrapper2_->Send(kTestAppID2, kUserID1, message3, TestGCMServiceWrapper::WAIT);
+
+ EXPECT_EQ(message3.id, wrapper2_->send_message_id());
+ EXPECT_EQ(GCMClient::SUCCESS, wrapper2_->send_result());
+}
+
+TEST_F(GCMServiceMultipleInstanceTest, MessageReceived) {
+ wrapper_->Register(kTestAppID1,
+ ToSenderList("sender"),
+ TestGCMServiceWrapper::WAIT);
+ wrapper2_->Register(kTestAppID1,
+ ToSenderList("sender"),
+ TestGCMServiceWrapper::WAIT);
+ wrapper2_->Register(kTestAppID2,
+ ToSenderList("sender2"),
+ TestGCMServiceWrapper::WAIT);
+
+ // Trigger an incoming message for an app in one instance.
+ GCMClient::IncomingMessage message;
+ message.data["key1"] = "value1";
+ message.data["key2"] = "value2";
+ message.sender_id = "sender";
+ wrapper_->GetGCMClient()->ReceiveMessage(kTestAppID1, message);
+ wrapper_->gcm_app_handler()->WaitForNotification();
+
+ // Trigger an incoming message for the same app in another instance.
+ GCMClient::IncomingMessage message2;
+ message2.data["foo"] = "bar";
+ message2.sender_id = "sender";
+ wrapper2_->GetGCMClient()->ReceiveMessage(kTestAppID1, message2);
+ wrapper2_->gcm_app_handler()->WaitForNotification();
+
+ EXPECT_EQ(FakeGCMAppHandler::MESSAGE_EVENT,
+ wrapper_->gcm_app_handler()->received_event());
+ EXPECT_EQ(kTestAppID1, wrapper_->gcm_app_handler()->app_id());
+ EXPECT_EQ(message.data, wrapper_->gcm_app_handler()->message().data);
+ EXPECT_EQ("sender", wrapper_->gcm_app_handler()->message().sender_id);
+
+ EXPECT_EQ(FakeGCMAppHandler::MESSAGE_EVENT,
+ wrapper2_->gcm_app_handler()->received_event());
+ EXPECT_EQ(kTestAppID1, wrapper2_->gcm_app_handler()->app_id());
+ EXPECT_EQ(message2.data, wrapper2_->gcm_app_handler()->message().data);
+ EXPECT_EQ("sender", wrapper2_->gcm_app_handler()->message().sender_id);
+
+ // Trigger another incoming message for a different app in another instance.
+ GCMClient::IncomingMessage message3;
+ message3.data["bar1"] = "foo1";
+ message3.data["bar2"] = "foo2";
+ message3.sender_id = "sender2";
+ wrapper2_->GetGCMClient()->ReceiveMessage(kTestAppID2, message3);
+ wrapper2_->gcm_app_handler()->WaitForNotification();
+
+ EXPECT_EQ(FakeGCMAppHandler::MESSAGE_EVENT,
+ wrapper2_->gcm_app_handler()->received_event());
+ EXPECT_EQ(kTestAppID2, wrapper2_->gcm_app_handler()->app_id());
+ EXPECT_EQ(message3.data, wrapper2_->gcm_app_handler()->message().data);
+ EXPECT_EQ("sender2", wrapper2_->gcm_app_handler()->message().sender_id);
+}
+
+// Test a set of GCM operations on multiple instances.
+// 1) Register 1 app in instance 1 and register 2 apps in instance 2;
+// 2) Send a message from instance 1;
+// 3) Receive a message to an app in instance 1 and receive a message for each
+// of the two apps in instance 2;
+// 4) Send a message for each of the two apps in instance 2;
+// 5) Sign out of instance 1.
+// 6) Register/send stops working for instance 1;
+// 7) The app in instance 2 can still receive these events;
+// 8) Sign into instance 1 with a different account.
+// 9) The message to the newly signed-in account will be routed.
+TEST_F(GCMServiceMultipleInstanceTest, Combined) {
+ // Register an app.
+ std::vector<std::string> sender_ids;
+ sender_ids.push_back("sender1");
+ wrapper_->Register(kTestAppID1, sender_ids, TestGCMServiceWrapper::WAIT);
+
+ // Register the same app in a different instance.
+ std::vector<std::string> sender_ids2;
+ sender_ids2.push_back("foo");
+ sender_ids2.push_back("bar");
+ wrapper2_->Register(kTestAppID1, sender_ids2, TestGCMServiceWrapper::WAIT);
+
+ // Register a different app in a different instance.
+ std::vector<std::string> sender_ids3;
+ sender_ids3.push_back("sender1");
+ sender_ids3.push_back("sender2");
+ sender_ids3.push_back("sender3");
+ wrapper2_->Register(kTestAppID2, sender_ids3, TestGCMServiceWrapper::WAIT);
+
+ EXPECT_EQ(GCMClientMock::GetRegistrationIdFromSenderIds(sender_ids),
+ wrapper_->registration_id());
+ EXPECT_EQ(GCMClient::SUCCESS, wrapper_->registration_result());
+
+ EXPECT_EQ(GCMClientMock::GetRegistrationIdFromSenderIds(sender_ids3),
+ wrapper2_->registration_id());
+ EXPECT_EQ(GCMClient::SUCCESS, wrapper2_->registration_result());
+
+ // Send a message from one instance.
+ GCMClient::OutgoingMessage out_message;
+ out_message.id = "1";
+ out_message.data["out1"] = "out_data1";
+ out_message.data["out1_2"] = "out_data1_2";
+ wrapper_->Send(kTestAppID1,
+ kUserID1,
+ out_message,
+ TestGCMServiceWrapper::WAIT);
+
+ EXPECT_EQ(out_message.id, wrapper_->send_message_id());
+ EXPECT_EQ(GCMClient::SUCCESS, wrapper_->send_result());
+
+ // Trigger an incoming message for an app in one instance.
+ GCMClient::IncomingMessage in_message;
+ in_message.data["in1"] = "in_data1";
+ in_message.data["in1_2"] = "in_data1_2";
+ in_message.sender_id = "sender1";
+ wrapper_->GetGCMClient()->ReceiveMessage(kTestAppID1, in_message);
+ wrapper_->gcm_app_handler()->WaitForNotification();
+
+ EXPECT_EQ(FakeGCMAppHandler::MESSAGE_EVENT,
+ wrapper_->gcm_app_handler()->received_event());
+ EXPECT_EQ(kTestAppID1, wrapper_->gcm_app_handler()->app_id());
+ EXPECT_EQ(in_message.data, wrapper_->gcm_app_handler()->message().data);
+
+ // Trigger 2 incoming messages, one for each app respectively, in another
+ // instance.
+ GCMClient::IncomingMessage in_message2;
+ in_message2.data["in2"] = "in_data2";
+ in_message2.sender_id = "sender3";
+ wrapper2_->GetGCMClient()->ReceiveMessage(kTestAppID2, in_message2);
+
+ GCMClient::IncomingMessage in_message3;
+ in_message3.data["in3"] = "in_data3";
+ in_message3.data["in3_2"] = "in_data3_2";
+ in_message3.sender_id = "foo";
+ wrapper2_->GetGCMClient()->ReceiveMessage(kTestAppID1, in_message3);
+
+ wrapper2_->gcm_app_handler()->WaitForNotification();
+
+ EXPECT_EQ(FakeGCMAppHandler::MESSAGE_EVENT,
+ wrapper2_->gcm_app_handler()->received_event());
+ EXPECT_EQ(kTestAppID2, wrapper2_->gcm_app_handler()->app_id());
+ EXPECT_EQ(in_message2.data, wrapper2_->gcm_app_handler()->message().data);
+
+ wrapper2_->gcm_app_handler()->WaitForNotification();
+
+ EXPECT_EQ(FakeGCMAppHandler::MESSAGE_EVENT,
+ wrapper2_->gcm_app_handler()->received_event());
+ EXPECT_EQ(kTestAppID1, wrapper2_->gcm_app_handler()->app_id());
+ EXPECT_EQ(in_message3.data, wrapper2_->gcm_app_handler()->message().data);
+
+ // Send two messages, one for each app respectively, from another instance.
+ GCMClient::OutgoingMessage out_message2;
+ out_message2.id = "2";
+ out_message2.data["out2"] = "out_data2";
+ wrapper2_->Send(kTestAppID1,
+ kUserID2,
+ out_message2,
+ TestGCMServiceWrapper::WAIT);
+
+ GCMClient::OutgoingMessage out_message3;
+ out_message3.id = "3";
+ out_message3.data["out3"] = "out_data3";
+ wrapper2_->Send(kTestAppID2,
+ kUserID2,
+ out_message3,
+ TestGCMServiceWrapper::DO_NOT_WAIT);
+
+ EXPECT_EQ(out_message2.id, wrapper2_->send_message_id());
+ EXPECT_EQ(GCMClient::SUCCESS, wrapper2_->send_result());
+
+ wrapper2_->WaitForAsyncOperation();
+
+ EXPECT_EQ(out_message3.id, wrapper2_->send_message_id());
+ EXPECT_EQ(GCMClient::SUCCESS, wrapper2_->send_result());
+
+ // Sign out of one instance.
+ wrapper_->SignOut();
+
+ // Register/send stops working for signed-out instance.
+ wrapper_->Register(kTestAppID1, sender_ids, TestGCMServiceWrapper::WAIT);
+ EXPECT_TRUE(wrapper_->registration_id().empty());
+ EXPECT_EQ(GCMClient::NOT_SIGNED_IN, wrapper_->registration_result());
+
+ wrapper_->Send(kTestAppID2,
+ kUserID2,
+ out_message3,
+ TestGCMServiceWrapper::WAIT);
+ EXPECT_TRUE(wrapper_->send_message_id().empty());
+ EXPECT_EQ(GCMClient::NOT_SIGNED_IN, wrapper_->send_result());
+
+ // Deleted messages event will go through for another signed-in instance.
+ wrapper2_->GetGCMClient()->DeleteMessages(kTestAppID2);
+ wrapper2_->gcm_app_handler()->WaitForNotification();
+
+ EXPECT_EQ(FakeGCMAppHandler::MESSAGES_DELETED_EVENT,
+ wrapper2_->gcm_app_handler()->received_event());
+ EXPECT_EQ(kTestAppID2, wrapper2_->gcm_app_handler()->app_id());
+
+ // Send error event will go through for another signed-in instance.
+ GCMClient::OutgoingMessage out_message4;
+ out_message4.id = "1@error";
+ out_message4.data["out4"] = "out_data4";
+ wrapper2_->Send(kTestAppID1,
+ kUserID1,
+ out_message4,
+ TestGCMServiceWrapper::WAIT);
+
+ EXPECT_EQ(out_message4.id, wrapper2_->send_message_id());
+ EXPECT_EQ(GCMClient::SUCCESS, wrapper2_->send_result());
+
+ wrapper2_->gcm_app_handler()->WaitForNotification();
+ EXPECT_EQ(FakeGCMAppHandler::SEND_ERROR_EVENT,
+ wrapper2_->gcm_app_handler()->received_event());
+ EXPECT_EQ(kTestAppID1, wrapper2_->gcm_app_handler()->app_id());
+ EXPECT_EQ(out_message4.id,
+ wrapper2_->gcm_app_handler()->send_error_details().message_id);
+ EXPECT_NE(GCMClient::SUCCESS,
+ wrapper2_->gcm_app_handler()->send_error_details().result);
+ EXPECT_EQ(out_message4.data,
+ wrapper2_->gcm_app_handler()->send_error_details().additional_data);
+
+ // Sign in with a different account.
+ wrapper_->SignIn(kTestAccountID3);
+
+ // Signing out cleared all registrations, so we need to register again.
+ wrapper_->Register(kTestAppID1,
+ ToSenderList("sender1"),
+ TestGCMServiceWrapper::WAIT);
+
+ // Incoming message will go through for the new signed-in account.
+ GCMClient::IncomingMessage in_message5;
+ in_message5.data["in5"] = "in_data5";
+ in_message5.sender_id = "sender1";
+ wrapper_->GetGCMClient()->ReceiveMessage(kTestAppID1, in_message5);
+
+ wrapper_->gcm_app_handler()->WaitForNotification();
+
+ EXPECT_EQ(FakeGCMAppHandler::MESSAGE_EVENT,
+ wrapper_->gcm_app_handler()->received_event());
+ EXPECT_EQ(in_message5.data, wrapper_->gcm_app_handler()->message().data);
+}
+
+} // namespace gcm
diff --git a/chrome/browser/signin/profile_identity_provider.cc b/chrome/browser/signin/profile_identity_provider.cc
index 7ae1a46..2a929935 100644
--- a/chrome/browser/signin/profile_identity_provider.cc
+++ b/chrome/browser/signin/profile_identity_provider.cc
@@ -4,9 +4,12 @@
#include "chrome/browser/signin/profile_identity_provider.h"
-#include "chrome/browser/ui/webui/signin/login_ui_service.h"
#include "components/signin/core/browser/profile_oauth2_token_service.h"
+#if !defined(OS_ANDROID)
+#include "chrome/browser/ui/webui/signin/login_ui_service.h"
+#endif
+
ProfileIdentityProvider::ProfileIdentityProvider(
SigninManagerBase* signin_manager,
ProfileOAuth2TokenService* token_service,
@@ -34,8 +37,12 @@ OAuth2TokenService* ProfileIdentityProvider::GetTokenService() {
}
bool ProfileIdentityProvider::RequestLogin() {
+#if defined(OS_ANDROID)
+ return false;
+#else
login_ui_service_->ShowLoginPopup();
return true;
+#endif
}
void ProfileIdentityProvider::GoogleSigninSucceeded(
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 4567f42..6ee7fa8 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -1997,6 +1997,8 @@
'browser/services/gcm/gcm_profile_service.h',
'browser/services/gcm/gcm_profile_service_factory.cc',
'browser/services/gcm/gcm_profile_service_factory.h',
+ 'browser/services/gcm/gcm_service.cc',
+ 'browser/services/gcm/gcm_service.h',
'browser/service_process/service_process_control.cc',
'browser/service_process/service_process_control_mac.mm',
'browser/service_process/service_process_control.h',
diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi
index d1d0766..b4af0a4 100644
--- a/chrome/chrome_tests_unit.gypi
+++ b/chrome/chrome_tests_unit.gypi
@@ -1252,11 +1252,14 @@
'browser/search_engines/template_url_prepopulate_data_unittest.cc',
'browser/search_engines/template_url_scraper_unittest.cc',
'browser/search_engines/template_url_unittest.cc',
+ 'browser/services/gcm/fake_gcm_client_factory.cc',
+ 'browser/services/gcm/fake_gcm_client_factory.h',
+ 'browser/services/gcm/fake_signin_manager.cc',
+ 'browser/services/gcm/fake_signin_manager.h',
'browser/services/gcm/gcm_client_mock.cc',
'browser/services/gcm/gcm_client_mock.h',
- 'browser/services/gcm/gcm_profile_service_test_helper.cc',
- 'browser/services/gcm/gcm_profile_service_test_helper.h',
'browser/services/gcm/gcm_profile_service_unittest.cc',
+ 'browser/services/gcm/gcm_service_unittest.cc',
'browser/sessions/persistent_tab_restore_service_unittest.cc',
'browser/sessions/restore_on_startup_policy_handler_unittest.cc',
'browser/sessions/session_backend_unittest.cc',
diff --git a/sync/notifier/gcm_network_channel.h b/sync/notifier/gcm_network_channel.h
index b2b8d6c..3f2afc9 100644
--- a/sync/notifier/gcm_network_channel.h
+++ b/sync/notifier/gcm_network_channel.h
@@ -45,7 +45,7 @@ struct GCMNetworkChannelDiagnostic {
};
// GCMNetworkChannel is an implementation of SyncNetworkChannel that routes
-// messages through GCMProfileService.
+// messages through GCMService.
class SYNC_EXPORT_PRIVATE GCMNetworkChannel
: public SyncNetworkChannel,
public net::URLFetcherDelegate,
diff --git a/sync/notifier/gcm_network_channel_delegate.h b/sync/notifier/gcm_network_channel_delegate.h
index ff371eb..36485a1 100644
--- a/sync/notifier/gcm_network_channel_delegate.h
+++ b/sync/notifier/gcm_network_channel_delegate.h
@@ -40,8 +40,8 @@ class GCMNetworkChannelDelegate {
// Invalidate access token that was rejected by server.
virtual void InvalidateToken(const std::string& token) = 0;
- // Request registration_id from GCMProfileService. Callback should be called
- // with either registration id or error code.
+ // Request registration_id from GCMService. Callback should be called with
+ // either registration id or error code.
virtual void Register(RegisterCallback callback) = 0;
// Provide callback for incoming messages from GCM.
virtual void SetMessageReceiver(MessageCallback callback) = 0;