From df84c5378331d859148ad916a706fe2d9be441fd Mon Sep 17 00:00:00 2001 From: "bartfab@chromium.org" Date: Fri, 25 Apr 2014 15:36:54 +0000 Subject: [PATCH] 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 --- .../browser/extensions/extension_gcm_app_handler.h | 6 + .../extension_gcm_app_handler_unittest.cc | 149 +- .../invalidation/gcm_invalidation_bridge.cc | 25 +- .../browser/invalidation/gcm_invalidation_bridge.h | 6 +- .../invalidation/ticl_invalidation_service.cc | 5 +- .../services/gcm/fake_gcm_client_factory.cc | 24 + .../browser/services/gcm/fake_gcm_client_factory.h | 34 + chrome/browser/services/gcm/fake_signin_manager.cc | 56 + chrome/browser/services/gcm/fake_signin_manager.h | 54 + chrome/browser/services/gcm/gcm_client_mock.cc | 2 +- chrome/browser/services/gcm/gcm_profile_service.cc | 1062 ++------------- chrome/browser/services/gcm/gcm_profile_service.h | 325 +---- .../services/gcm/gcm_profile_service_factory.cc | 10 + .../services/gcm/gcm_profile_service_factory.h | 3 +- .../gcm/gcm_profile_service_test_helper.cc | 119 -- .../services/gcm/gcm_profile_service_test_helper.h | 94 -- .../services/gcm/gcm_profile_service_unittest.cc | 1433 +++----------------- .../gcm/{gcm_profile_service.cc => gcm_service.cc} | 535 +++----- .../gcm/{gcm_profile_service.h => gcm_service.h} | 131 +- .../browser/services/gcm/gcm_service_unittest.cc | 1318 ++++++++++++++++++ chrome/browser/signin/profile_identity_provider.cc | 9 +- chrome/chrome_browser.gypi | 2 + chrome/chrome_tests_unit.gypi | 7 +- sync/notifier/gcm_network_channel.h | 2 +- sync/notifier/gcm_network_channel_delegate.h | 4 +- 25 files changed, 2303 insertions(+), 3112 deletions(-) create mode 100644 chrome/browser/services/gcm/fake_gcm_client_factory.cc create mode 100644 chrome/browser/services/gcm/fake_gcm_client_factory.h create mode 100644 chrome/browser/services/gcm/fake_signin_manager.cc create mode 100644 chrome/browser/services/gcm/fake_signin_manager.h rewrite chrome/browser/services/gcm/gcm_profile_service.cc (91%) rewrite chrome/browser/services/gcm/gcm_profile_service.h (80%) delete mode 100644 chrome/browser/services/gcm/gcm_profile_service_test_helper.cc delete mode 100644 chrome/browser/services/gcm/gcm_profile_service_test_helper.h rewrite chrome/browser/services/gcm/gcm_profile_service_unittest.cc (96%) copy chrome/browser/services/gcm/{gcm_profile_service.cc => gcm_service.cc} (57%) copy chrome/browser/services/gcm/{gcm_profile_service.h => gcm_service.h} (62%) create mode 100644 chrome/browser/services/gcm/gcm_service_unittest.cc diff --git a/chrome/browser/extensions/extension_gcm_app_handler.h b/chrome/browser/extensions/extension_gcm_app_handler.h index 8e90ddf344df..9a7298d95ebb 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 + +#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 1adc3dc21f20..6b5726191be7 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 + #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 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(context)); + return new gcm::GCMProfileService(static_cast(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( - GCMProfileServiceFactory::GetInstance()->SetTestingFactoryAndUse( - profile(), - &ExtensionGCMAppHandlerTest::BuildGCMProfileService)); - scoped_ptr gcm_client_factory( - new FakeGCMClientFactory(GCMClientMock::NO_DELAY_LOADING)); + gcm::GCMProfileService* gcm_profile_service = + static_cast( + gcm::GCMProfileServiceFactory::GetInstance()-> + SetTestingFactoryAndUse( + profile(), + &ExtensionGCMAppHandlerTest::BuildGCMProfileService)); + scoped_ptr 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 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 5925eda929e7..88d09719bbc2 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 @@ -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 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 c72c6a11b779..a9d781e478b3 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_; diff --git a/chrome/browser/invalidation/ticl_invalidation_service.cc b/chrome/browser/invalidation/ticl_invalidation_service.cc index 8db91112eed2..3eecec9664d5 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 000000000000..2d1d23c8c3d4 --- /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 FakeGCMClientFactory::BuildInstance() { + return scoped_ptr(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 000000000000..ba6c74a12de4 --- /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 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 000000000000..967c0b9d9ee7 --- /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 000000000000..fc39509ae5e8 --- /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 + +#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 d92423800597..b894efaa70c8 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& sender_ids) { - // GCMProfileService normalizes the sender IDs by making them sorted. + // GCMService normalizes the sender IDs by making them sorted. std::vector 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 dissimilarity index 91% index f93b427cf9ba..0592b0c76483 100644 --- a/chrome/browser/services/gcm/gcm_profile_service.cc +++ b/chrome/browser/services/gcm/gcm_profile_service.cc @@ -1,947 +1,115 @@ -// Copyright (c) 2013 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 -#include -#include - -#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 "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_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 "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 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 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{ - 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 gcm_client_factory, - const base::FilePath& store_path, - const std::vector& account_ids, - const scoped_refptr& - url_request_context_getter); - void Reset(); - void Load(const base::WeakPtr& service); - void Stop(); - void CheckOut(); - void Register(const std::string& app_id, - const std::vector& 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; - virtual ~IOWorker(); - - base::WeakPtr service_; - - scoped_ptr gcm_client_; -}; - -GCMProfileService::IOWorker::IOWorker() { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); -} - -GCMProfileService::IOWorker::~IOWorker() { -} - -void GCMProfileService::IOWorker::Initialize( - scoped_ptr gcm_client_factory, - const base::FilePath& store_path, - const std::vector& account_ids, - const scoped_refptr& - 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 worker_pool( - content::BrowserThread::GetBlockingPool()); - scoped_refptr 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& 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& 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)); -} - -void GCMProfileService::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(&GCMProfileService::GetGCMStatisticsFinished, - service_, - stats)); -} - -std::string GCMProfileService::GetGCMEnabledStateString(GCMEnabledState state) { - switch (state) { - case GCMProfileService::ALWAYS_ENABLED: - return "ALWAYS_ENABLED"; - case GCMProfileService::ENABLED_FOR_APPS: - return "ENABLED_FOR_APPS"; - case GCMProfileService::ALWAYS_DISABLED: - return "ALWAYS_DISABLED"; - default: - NOTREACHED(); - return std::string(); - } -} - -// 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. - chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel(); - bool on_by_default = false; - if (channel == chrome::VersionInfo::CHANNEL_UNKNOWN || - channel == chrome::VersionInfo::CHANNEL_CANARY || - channel == chrome::VersionInfo::CHANNEL_DEV) { - on_by_default = true; - } - registry->RegisterBooleanPref( - prefs::kGCMChannelEnabled, - on_by_default, - user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); -} - -GCMProfileService::GCMProfileService(Profile* profile) - : profile_(profile), - gcm_client_ready_(false), - weak_ptr_factory_(this) { - DCHECK(!profile->IsOffTheRecord()); -} - -GCMProfileService::~GCMProfileService() { -} - -void GCMProfileService::Initialize( - scoped_ptr gcm_client_factory) { - registrar_.Add(this, - chrome::NOTIFICATION_PROFILE_DESTROYED, - content::Source(profile_)); - - SigninManagerFactory::GetForProfile(profile_)->AddObserver(this); - - // Get the list of available accounts. - std::vector 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 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& 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& sender_ids) { - std::map::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 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 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; -} - -std::string GCMProfileService::SignedInUserName() const { - return username_; -} - -bool GCMProfileService::IsGCMClientReady() const { - return gcm_client_ready_; -} - -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::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::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, SendCallback>::iterator - callback_iter = send_callbacks_.find( - std::pair(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(); -} - -GCMAppHandler* GCMProfileService::GetAppHandler(const std::string& app_id) { - std::map::const_iterator iter = - app_handlers_.find(app_id); - return iter == app_handlers_.end() ? &default_app_handler_ : iter->second; -} - -void GCMProfileService::GetGCMStatisticsFinished( - GCMClient::GCMStatistics stats) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); - request_gcm_statistics_callback_.Run(stats); -} - -} // namespace gcm +// Copyright (c) 2013 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 "base/files/file_path.h" +#include "base/logging.h" +#include "base/prefs/pref_service.h" +#include "base/values.h" +#include "chrome/browser/profiles/profile.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_version_info.h" +#include "chrome/common/pref_names.h" +#include "components/signin/core/browser/signin_manager.h" +#include "components/user_prefs/pref_registry_syncable.h" +#include "google_apis/gaia/identity_provider.h" +#include "net/url_request/url_request_context_getter.h" + +#if !defined(OS_ANDROID) +#include "chrome/browser/ui/webui/signin/login_ui_service_factory.h" +#endif + +namespace gcm { + +// 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 +std::string GCMProfileService::GetGCMEnabledStateString(GCMEnabledState state) { + switch (state) { + case GCMProfileService::ALWAYS_ENABLED: + return "ALWAYS_ENABLED"; + case GCMProfileService::ENABLED_FOR_APPS: + return "ENABLED_FOR_APPS"; + case GCMProfileService::ALWAYS_DISABLED: + return "ALWAYS_DISABLED"; + default: + NOTREACHED(); + return std::string(); + } +} + +// static +void GCMProfileService::RegisterProfilePrefs( + user_prefs::PrefRegistrySyncable* registry) { + // GCM support is only enabled by default for Canary/Dev/Custom builds. + chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel(); + bool on_by_default = false; + if (channel == chrome::VersionInfo::CHANNEL_UNKNOWN || + channel == chrome::VersionInfo::CHANNEL_CANARY || + channel == chrome::VersionInfo::CHANNEL_DEV) { + on_by_default = true; + } + registry->RegisterBooleanPref( + prefs::kGCMChannelEnabled, + on_by_default, + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); +} + +GCMProfileService::GCMProfileService(Profile* profile) + : GCMService(scoped_ptr(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::Shutdown() { + ShutdownService(); +} + +std::string GCMProfileService::SignedInUserName() const { + if (IsStarted()) + return identity_provider_->GetActiveUsername(); + return std::string(); +} + +bool GCMProfileService::ShouldStartAutomatically() const { + return GetGCMEnabledState(profile_) == ALWAYS_ENABLED; +} + +base::FilePath GCMProfileService::GetStorePath() const { + return profile_->GetPath().Append(chrome::kGCMStoreDirname); +} + +scoped_refptr +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 dissimilarity index 80% index b93bfa2b3066..56029b566bb4 100644 --- a/chrome/browser/services/gcm/gcm_profile_service.h +++ b/chrome/browser/services/gcm/gcm_profile_service.h @@ -1,252 +1,73 @@ -// Copyright (c) 2013 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_H_ -#define CHROME_BROWSER_SERVICES_GCM_GCM_PROFILE_SERVICE_H_ - -#include -#include - -#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 "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 { - public: - typedef base::Callback RegisterCallback; - typedef base::Callback SendCallback; - typedef base::Callback UnregisterCallback; - typedef base::Callback - GetGCMStatisticsCallback; - - // Any change made to this enum should have corresponding change in the - // GetGCMEnabledStateString(...) function. - enum GCMEnabledState { - // GCM is always enabled. GCMClient will always load and connect with GCM. - ALWAYS_ENABLED, - // GCM is only enabled for apps. GCMClient will start to load and connect - // with GCM only when GCM API is used. - ENABLED_FOR_APPS, - // GCM is always disabled. GCMClient will never load and connect with GCM. - ALWAYS_DISABLED - }; - - // Returns the GCM enabled state. - static GCMEnabledState GetGCMEnabledState(Profile* profile); - - // Returns text representation of a GCMEnabledState enum entry. - static std::string GetGCMEnabledStateString(GCMEnabledState state); - - // Register profile-specific prefs for GCM. - static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry); - - explicit GCMProfileService(Profile* profile); - virtual ~GCMProfileService(); - - void Initialize(scoped_ptr gcm_client_factory); - - void Start(); - - void Stop(); - - // KeyedService implementation. - 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& 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); - - private: - friend class GCMProfileServiceTestConsumer; - friend class extensions::ExtensionGCMAppHandlerTest; - - class DelayedTaskController; - class IOWorker; - - typedef std::map 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& 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 delayed_task_controller_; - - // For all the work occured in IO thread. - scoped_refptr 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 register_callbacks_; - - // Callback map (from app_id to callback) for Unregister. - std::map unregister_callbacks_; - - // Callback map (from to callback) for Send. - std::map, SendCallback> send_callbacks_; - - // Callback for GetGCMStatistics. - GetGCMStatisticsCallback request_gcm_statistics_callback_; - - // Used to pass a weak pointer to the IO worker. - base::WeakPtrFactory weak_ptr_factory_; - - DISALLOW_COPY_AND_ASSIGN(GCMProfileService); -}; - -} // namespace gcm - -#endif // CHROME_BROWSER_SERVICES_GCM_GCM_PROFILE_SERVICE_H_ +// Copyright (c) 2013 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_H_ +#define CHROME_BROWSER_SERVICES_GCM_GCM_PROFILE_SERVICE_H_ + +#include + +#include "base/compiler_specific.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" + +class Profile; + +namespace user_prefs { +class PrefRegistrySyncable; +} + +namespace gcm { + +// A specialization of GCMService that is tied to a Profile. +class GCMProfileService : public GCMService, public KeyedService { + public: + // Any change made to this enum should have corresponding change in the + // GetGCMEnabledStateString(...) function. + enum GCMEnabledState { + // GCM is always enabled. GCMClient will always load and connect with GCM. + ALWAYS_ENABLED, + // GCM is only enabled for apps. GCMClient will start to load and connect + // with GCM only when GCM API is used. + ENABLED_FOR_APPS, + // GCM is always disabled. GCMClient will never load and connect with GCM. + ALWAYS_DISABLED + }; + + // Returns the GCM enabled state. + static GCMEnabledState GetGCMEnabledState(Profile* profile); + + // Returns text representation of a GCMEnabledState enum entry. + static std::string GetGCMEnabledStateString(GCMEnabledState state); + + // Register profile-specific prefs for GCM. + static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry); + + explicit GCMProfileService(Profile* profile); + virtual ~GCMProfileService(); + + // KeyedService: + virtual void Shutdown() OVERRIDE; + + // Returns the user name if the profile is signed in. + std::string SignedInUserName() const; + + protected: + // Overridden from GCMService: + virtual bool ShouldStartAutomatically() const OVERRIDE; + virtual base::FilePath GetStorePath() const OVERRIDE; + virtual scoped_refptr + GetURLRequestContextGetter() const OVERRIDE; + + private: + // The profile which owns this object. + Profile* profile_; + + DISALLOW_COPY_AND_ASSIGN(GCMProfileService); +}; + +} // namespace gcm + +#endif // CHROME_BROWSER_SERVICES_GCM_GCM_PROFILE_SERVICE_H_ diff --git a/chrome/browser/services/gcm/gcm_profile_service_factory.cc b/chrome/browser/services/gcm/gcm_profile_service_factory.cc index 2a5b4c5ec003..437fa5f92396 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 be27b7d8af9b..2e5763ea4f3f 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 2379482220c6..000000000000 --- 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(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 FakeGCMClientFactory::BuildInstance() { - return scoped_ptr(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 66eea37b7307..000000000000 --- 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 -#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 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 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 dissimilarity index 96% index 9be82fea6457..6fef486dbb9f 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. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/bind.h" -#include "base/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 "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 "content/public/test/test_browser_thread_bundle.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"; - -std::vector ToSenderList(const std::string& sender_ids) { - std::vector senders; - Tokenize(sender_ids, ",", &senders); - return senders; -} - -} // 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(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( - 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( - GCMProfileServiceFactory::GetInstance()->SetTestingFactoryAndUse( - profile(), &GCMProfileServiceTestConsumer::BuildGCMProfileService)); - scoped_ptr 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& 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( - 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(); - } - - Profile* profile() const { return profile_.get(); } - FakeGCMAppHandler* gcm_app_handler() const { - return gcm_app_handler_.get(); - } - - void set_gcm_client_loading_delay(GCMClientMock::LoadingDelay delay) { - gcm_client_loading_delay_ = delay; - } - - 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 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; - } - - private: - Waiter* waiter_; // Not owned. - scoped_ptr profile_; - FakeSigninManager* signin_manager_; // Not owned. - scoped_ptr gcm_app_handler_; - - GCMClientMock::LoadingDelay gcm_client_loading_delay_; - - 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 thread_bundle_; - scoped_ptr 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 incognito_profile = - incognito_profile_builder.Build(); - incognito_profile->SetOriginalProfile(profile()); - - EXPECT_FALSE(GCMProfileServiceFactory::GetForProfile( - incognito_profile.get())); -} - -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()); -} - -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()); -} - -TEST_F(GCMProfileServiceTest, Shutdown) { - consumer()->CreateGCMProfileServiceInstance(); - EXPECT_TRUE(consumer()->HasAppHandlers()); - - consumer()->GetGCMProfileService()->Shutdown(); - EXPECT_FALSE(consumer()->HasAppHandlers()); -} - -TEST_F(GCMProfileServiceTest, SignInAndSignOutUnderPositiveChannelSignal) { - // 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()); -} - -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()); -} - -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()); -} - -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()); -} - -TEST_F(GCMProfileServiceTest, RegisterWhenNotSignedIn) { - consumer()->CreateGCMProfileServiceInstance(); - - std::vector 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(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()); - - // Invoking register will make GCMClient checked in. - std::vector sender_ids; - sender_ids.push_back("sender1"); - consumer()->Register(kTestingAppId, sender_ids); - WaitUntilCompleted(); - - // GCMClient should be checked in. - EXPECT_TRUE(consumer()->IsGCMClientReady()); - EXPECT_EQ(GCMClientMock::LOADED, consumer()->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()); -} - -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()); - - // Invoking send will make GCMClient checked in. - GCMClient::OutgoingMessage message; - message.id = "1"; - message.data["key1"] = "value1"; - consumer()->Send(kTestingAppId, kUserId, message); - WaitUntilCompleted(); - - // GCMClient should be checked in. - EXPECT_TRUE(consumer()->IsGCMClientReady()); - EXPECT_EQ(GCMClientMock::LOADED, consumer()->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 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 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 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 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 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 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 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 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 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 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 consumer2_; -}; - -TEST_F(GCMProfileServiceMultiProfileTest, Register) { - // Register an app. - std::vector sender_ids; - sender_ids.push_back("sender1"); - consumer()->Register(kTestingAppId, sender_ids); - - // Register the same app in a different profile. - std::vector 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 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 sender_ids; - sender_ids.push_back("sender1"); - consumer()->Register(kTestingAppId, sender_ids); - - // Register the same app in a different profile. - std::vector 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 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); -} - -} // namespace gcm +// 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 + +#include "base/bind.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_factory.h" +#include "chrome/browser/signin/signin_manager_factory.h" +#include "chrome/test/base/testing_profile.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 kTestAccountID[] = "user@example.com"; +const char kTestAppID[] = "TestApp"; +const char kUserID[] = "user"; + +KeyedService* BuildGCMProfileService(content::BrowserContext* context) { + return new GCMProfileService(Profile::FromBrowserContext(context)); +} + +} // namespace + +class GCMProfileServiceTest : public testing::Test { + protected: + GCMProfileServiceTest(); + virtual ~GCMProfileServiceTest(); + + // testing::Test: + virtual void SetUp() OVERRIDE; + + GCMClientMock* GetGCMClient() const; + + void RegisterAndWaitForCompletion(const std::vector& sender_ids); + void SendAndWaitForCompletion(const GCMClient::OutgoingMessage& message); + + 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); + + content::TestBrowserThreadBundle thread_bundle_; + scoped_ptr profile_; + GCMProfileService* gcm_profile_service_; + + std::string registration_id_; + GCMClient::Result registration_result_; + std::string send_message_id_; + GCMClient::Result send_result_; + + private: + DISALLOW_COPY_AND_ASSIGN(GCMProfileServiceTest); +}; + +GCMProfileServiceTest::GCMProfileServiceTest() + : gcm_profile_service_(NULL), + registration_result_(GCMClient::UNKNOWN_ERROR), + send_result_(GCMClient::UNKNOWN_ERROR) { +} + +GCMProfileServiceTest::~GCMProfileServiceTest() { +} + +GCMClientMock* GCMProfileServiceTest::GetGCMClient() const { + return static_cast( + gcm_profile_service_->GetGCMClientForTesting()); +} + +void GCMProfileServiceTest::SetUp() { + TestingProfile::Builder builder; + builder.AddTestingFactory(SigninManagerFactory::GetInstance(), + FakeSigninManager::Build); + profile_ = builder.Build(); + + gcm_profile_service_ = static_cast( + GCMProfileServiceFactory::GetInstance()->SetTestingFactoryAndUse( + profile_.get(), + &BuildGCMProfileService)); + gcm_profile_service_->Initialize(scoped_ptr( + new FakeGCMClientFactory(GCMClientMock::NO_DELAY_LOADING))); + + FakeSigninManager* signin_manager = static_cast( + SigninManagerFactory::GetInstance()->GetForProfile(profile_.get())); + signin_manager->SignIn(kTestAccountID); + base::RunLoop().RunUntilIdle(); +} + +void GCMProfileServiceTest::RegisterAndWaitForCompletion( + const std::vector& 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(); +} + +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(); +} + +void GCMProfileServiceTest::RegisterCompleted( + const base::Closure& callback, + const std::string& registration_id, + GCMClient::Result result) { + registration_id_ = registration_id; + registration_result_ = result; + callback.Run(); +} + +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) { + // GCMClient should not be checked in. + EXPECT_FALSE(gcm_profile_service_->IsGCMClientReady()); + EXPECT_EQ(GCMClientMock::UNINITIALIZED, GetGCMClient()->status()); + + // Invoking register will make GCMClient checked in. + std::vector sender_ids; + sender_ids.push_back("sender"); + RegisterAndWaitForCompletion(sender_ids); + + // GCMClient should be checked in. + 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, registration_id_); + EXPECT_EQ(GCMClient::SUCCESS, registration_result_); +} + +TEST_F(GCMProfileServiceTest, SendUnderNeutralChannelSignal) { + // GCMClient should not be checked in. + 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"; + SendAndWaitForCompletion( message); + + // GCMClient should be checked in. + EXPECT_TRUE(gcm_profile_service_->IsGCMClientReady()); + EXPECT_EQ(GCMClientMock::LOADED, GetGCMClient()->status()); + + // Sending should succeed. + EXPECT_EQ(message.id, send_message_id_); + EXPECT_EQ(GCMClient::SUCCESS, send_result_); +} + +} // namespace gcm diff --git a/chrome/browser/services/gcm/gcm_profile_service.cc b/chrome/browser/services/gcm/gcm_service.cc similarity index 57% copy from chrome/browser/services/gcm/gcm_profile_service.cc copy to chrome/browser/services/gcm/gcm_service.cc index f93b427cf9ba..0b5c74dc601e 100644 --- a/chrome/browser/services/gcm/gcm_profile_service.cc +++ b/chrome/browser/services/gcm/gcm_service.cc @@ -1,36 +1,23 @@ -// 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 "chrome/browser/services/gcm/gcm_service.h" #include -#include -#include +#include -#include "base/base64.h" -#include "base/files/file_path.h" +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/location.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/sequenced_task_runner.h" #include "base/threading/sequenced_worker_pool.h" -#include "chrome/browser/chrome_notification_types.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_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/gaia/oauth2_token_service.h" #include "google_apis/gcm/protocol/android_checkin.pb.h" #include "net/url_request/url_request_context_getter.h" @@ -82,13 +69,13 @@ checkin_proto::ChromeBuildProto_Channel GetChannel() { } // namespace // Helper class to save tasks to run until we're ready to execute them. -class GCMProfileService::DelayedTaskController { +class GCMService::DelayedTaskController { public: DelayedTaskController(); ~DelayedTaskController(); // Adds a task that will be invoked once we're ready. - void AddTask(base::Closure task); + void AddTask(const base::Closure& task); // Sets ready status. It is ready only when check-in is completed and // the GCMClient is fully initialized. @@ -104,29 +91,30 @@ class GCMProfileService::DelayedTaskController { bool ready_; std::vector delayed_tasks_; + + DISALLOW_COPY_AND_ASSIGN(DelayedTaskController); }; -GCMProfileService::DelayedTaskController::DelayedTaskController() - : ready_(false) { +GCMService::DelayedTaskController::DelayedTaskController() : ready_(false) { } -GCMProfileService::DelayedTaskController::~DelayedTaskController() { +GCMService::DelayedTaskController::~DelayedTaskController() { } -void GCMProfileService::DelayedTaskController::AddTask(base::Closure task) { +void GCMService::DelayedTaskController::AddTask(const base::Closure& task) { delayed_tasks_.push_back(task); } -void GCMProfileService::DelayedTaskController::SetReady() { +void GCMService::DelayedTaskController::SetReady() { ready_ = true; RunTasks(); } -bool GCMProfileService::DelayedTaskController::CanRunTaskWithoutDelay() const { +bool GCMService::DelayedTaskController::CanRunTaskWithoutDelay() const { return ready_; } -void GCMProfileService::DelayedTaskController::RunTasks() { +void GCMService::DelayedTaskController::RunTasks() { DCHECK(ready_); for (size_t i = 0; i < delayed_tasks_.size(); ++i) @@ -134,12 +122,11 @@ void GCMProfileService::DelayedTaskController::RunTasks() { delayed_tasks_.clear(); } -class GCMProfileService::IOWorker - : public GCMClient::Delegate, - public base::RefCountedThreadSafe{ +class GCMService::IOWorker : public GCMClient::Delegate { public: // Called on UI thread. IOWorker(); + virtual ~IOWorker(); // Overridden from GCMClient::Delegate: // Called on IO thread. @@ -166,8 +153,7 @@ class GCMProfileService::IOWorker const std::vector& account_ids, const scoped_refptr& url_request_context_getter); - void Reset(); - void Load(const base::WeakPtr& service); + void Load(const base::WeakPtr& service); void Stop(); void CheckOut(); void Register(const std::string& app_id, @@ -183,22 +169,22 @@ class GCMProfileService::IOWorker GCMClient* gcm_client_for_testing() const { return gcm_client_.get(); } private: - friend class base::RefCountedThreadSafe; - virtual ~IOWorker(); - - base::WeakPtr service_; + base::WeakPtr service_; scoped_ptr gcm_client_; + + DISALLOW_COPY_AND_ASSIGN(IOWorker); }; -GCMProfileService::IOWorker::IOWorker() { +GCMService::IOWorker::IOWorker() { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); } -GCMProfileService::IOWorker::~IOWorker() { +GCMService::IOWorker::~IOWorker() { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); } -void GCMProfileService::IOWorker::Initialize( +void GCMService::IOWorker::Initialize( scoped_ptr gcm_client_factory, const base::FilePath& store_path, const std::vector& account_ids, @@ -228,129 +214,111 @@ void GCMProfileService::IOWorker::Initialize( 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( +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(&GCMProfileService::RegisterFinished, - service_, - app_id, - registration_id, - result)); + content::BrowserThread::PostTask(content::BrowserThread::UI, + FROM_HERE, + base::Bind(&GCMService::RegisterFinished, + service_, + app_id, + registration_id, + result)); } -void GCMProfileService::IOWorker::OnUnregisterFinished( - const std::string& app_id, - GCMClient::Result 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( - &GCMProfileService::UnregisterFinished, service_, app_id, result)); + base::Bind(&GCMService::UnregisterFinished, service_, app_id, result)); } -void GCMProfileService::IOWorker::OnSendFinished( - const std::string& app_id, - const std::string& message_id, - GCMClient::Result 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(&GCMProfileService::SendFinished, - service_, - app_id, - message_id, - result)); + content::BrowserThread::PostTask(content::BrowserThread::UI, + FROM_HERE, + base::Bind(&GCMService::SendFinished, + service_, + app_id, + message_id, + result)); } -void GCMProfileService::IOWorker::OnMessageReceived( +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(&GCMProfileService::MessageReceived, - service_, - app_id, - message)); + content::BrowserThread::PostTask(content::BrowserThread::UI, + FROM_HERE, + base::Bind(&GCMService::MessageReceived, + service_, + app_id, + message)); } -void GCMProfileService::IOWorker::OnMessagesDeleted(const std::string& app_id) { +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(&GCMProfileService::MessagesDeleted, - service_, - app_id)); + content::BrowserThread::PostTask(content::BrowserThread::UI, + FROM_HERE, + base::Bind(&GCMService::MessagesDeleted, + service_, + app_id)); } -void GCMProfileService::IOWorker::OnMessageSendError( +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(&GCMProfileService::MessageSendError, - service_, - app_id, - send_error_details)); + content::BrowserThread::PostTask(content::BrowserThread::UI, + FROM_HERE, + base::Bind(&GCMService::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 GCMService::IOWorker::OnGCMReady() { + content::BrowserThread::PostTask(content::BrowserThread::UI, + FROM_HERE, + base::Bind(&GCMService::GCMClientReady, + service_)); } -void GCMProfileService::IOWorker::Load( - const base::WeakPtr& service) { +void GCMService::IOWorker::Load(const base::WeakPtr& service) { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); service_ = service; gcm_client_->Load(); } -void GCMProfileService::IOWorker::Stop() { +void GCMService::IOWorker::Stop() { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); gcm_client_->Stop(); } -void GCMProfileService::IOWorker::CheckOut() { +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 profile - // might be signed in again. + // Note that we still need to keep GCMClient instance alive since the + // GCMService may check in again. } -void GCMProfileService::IOWorker::Register( +void GCMService::IOWorker::Register( const std::string& app_id, const std::vector& sender_ids) { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); @@ -358,22 +326,21 @@ void GCMProfileService::IOWorker::Register( gcm_client_->Register(app_id, sender_ids); } -void GCMProfileService::IOWorker::Unregister(const std::string& app_id) { +void GCMService::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) { +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 GCMProfileService::IOWorker::GetGCMStatistics(bool clear_logs) { +void GCMService::IOWorker::GetGCMStatistics(bool clear_logs) { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); gcm::GCMClient::GCMStatistics stats; @@ -386,12 +353,10 @@ void GCMProfileService::IOWorker::GetGCMStatistics(bool clear_logs) { content::BrowserThread::PostTask( content::BrowserThread::UI, FROM_HERE, - base::Bind(&GCMProfileService::GetGCMStatisticsFinished, - service_, - stats)); + base::Bind(&GCMService::GetGCMStatisticsFinished, service_, stats)); } -void GCMProfileService::IOWorker::SetGCMRecording(bool recording) { +void GCMService::IOWorker::SetGCMRecording(bool recording) { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); gcm::GCMClient::GCMStatistics stats; @@ -404,114 +369,58 @@ void GCMProfileService::IOWorker::SetGCMRecording(bool recording) { content::BrowserThread::PostTask( content::BrowserThread::UI, FROM_HERE, - base::Bind(&GCMProfileService::GetGCMStatisticsFinished, - service_, - stats)); -} - -std::string GCMProfileService::GetGCMEnabledStateString(GCMEnabledState state) { - switch (state) { - case GCMProfileService::ALWAYS_ENABLED: - return "ALWAYS_ENABLED"; - case GCMProfileService::ENABLED_FOR_APPS: - return "ENABLED_FOR_APPS"; - case GCMProfileService::ALWAYS_DISABLED: - return "ALWAYS_DISABLED"; - default: - NOTREACHED(); - return std::string(); - } + base::Bind(&GCMService::GetGCMStatisticsFinished, service_, 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; - - 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. - chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel(); - bool on_by_default = false; - if (channel == chrome::VersionInfo::CHANNEL_UNKNOWN || - channel == chrome::VersionInfo::CHANNEL_CANARY || - channel == chrome::VersionInfo::CHANNEL_DEV) { - on_by_default = true; - } - registry->RegisterBooleanPref( - prefs::kGCMChannelEnabled, - on_by_default, - user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); -} - -GCMProfileService::GCMProfileService(Profile* profile) - : profile_(profile), +GCMService::GCMService(scoped_ptr identity_provider) + : identity_provider_(identity_provider.Pass()), gcm_client_ready_(false), weak_ptr_factory_(this) { - DCHECK(!profile->IsOffTheRecord()); } -GCMProfileService::~GCMProfileService() { +GCMService::~GCMService() { } -void GCMProfileService::Initialize( - scoped_ptr gcm_client_factory) { - registrar_.Add(this, - chrome::NOTIFICATION_PROFILE_DESTROYED, - content::Source(profile_)); - - SigninManagerFactory::GetForProfile(profile_)->AddObserver(this); - +void GCMService::Initialize(scoped_ptr gcm_client_factory) { // Get the list of available accounts. std::vector account_ids; #if !defined(OS_ANDROID) - account_ids = - ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)->GetAccounts(); + account_ids = identity_provider_->GetTokenService()->GetAccounts(); #endif // Create and initialize the GCMClient. Note that this does not initiate the // GCM check-in. - io_worker_ = new IOWorker(); - scoped_refptr url_request_context_getter = - profile_->GetRequestContext(); + DCHECK(!io_worker_); + io_worker_.reset(new IOWorker()); content::BrowserThread::PostTask( content::BrowserThread::IO, FROM_HERE, - base::Bind(&GCMProfileService::IOWorker::Initialize, - io_worker_, + base::Bind(&GCMService::IOWorker::Initialize, + base::Unretained(io_worker_.get()), base::Passed(&gcm_client_factory), - profile_->GetPath().Append(chrome::kGCMStoreDirname), + GetStorePath(), account_ids, - url_request_context_getter)); + GetURLRequestContextGetter())); // Load from the GCM store and initiate the GCM check-in if the rollout signal // indicates yes. - if (GetGCMEnabledState(profile_) == ALWAYS_ENABLED) + if (ShouldStartAutomatically()) EnsureLoaded(); + + identity_provider_->AddObserver(this); } -void GCMProfileService::Start() { +void GCMService::Start() { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); EnsureLoaded(); } -void GCMProfileService::Stop() { +void GCMService::Stop() { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); // No need to stop GCM service if not started yet. - if (username_.empty()) + if (account_id_.empty()) return; RemoveCachedData(); @@ -519,21 +428,26 @@ void GCMProfileService::Stop() { content::BrowserThread::PostTask( content::BrowserThread::IO, FROM_HERE, - base::Bind(&GCMProfileService::IOWorker::Stop, io_worker_)); + base::Bind(&GCMService::IOWorker::Stop, + base::Unretained(io_worker_.get()))); } -void GCMProfileService::Shutdown() { +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(); - - SigninManagerFactory::GetForProfile(profile_)->RemoveObserver(this); + content::BrowserThread::DeleteSoon(content::BrowserThread::IO, + FROM_HERE, + io_worker_.release()); } -void GCMProfileService::AddAppHandler(const std::string& app_id, - GCMAppHandler* handler) { +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()); @@ -541,17 +455,20 @@ void GCMProfileService::AddAppHandler(const std::string& app_id, app_handlers_[app_id] = handler; } -void GCMProfileService::RemoveAppHandler(const std::string& app_id) { +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 GCMProfileService::Register(const std::string& app_id, - const std::vector& sender_ids, - RegisterCallback callback) { +void GCMService::Register(const std::string& app_id, + const std::vector& sender_ids, + RegisterCallback callback) { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); - DCHECK(!app_id.empty() && !sender_ids.empty() && !callback.is_null()); + DCHECK(!app_id.empty()); + DCHECK(!sender_ids.empty()); + DCHECK(!callback.is_null()); GCMClient::Result result = EnsureAppReady(app_id); if (result != GCMClient::SUCCESS) { @@ -569,19 +486,19 @@ void GCMProfileService::Register(const std::string& app_id, // 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)); + delayed_task_controller_->AddTask(base::Bind(&GCMService::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& sender_ids) { +void GCMService::DoRegister(const std::string& app_id, + const std::vector& sender_ids) { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); std::map::iterator callback_iter = register_callbacks_.find(app_id); if (callback_iter == register_callbacks_.end()) { @@ -596,16 +513,17 @@ void GCMProfileService::DoRegister(const std::string& app_id, content::BrowserThread::PostTask( content::BrowserThread::IO, FROM_HERE, - base::Bind(&GCMProfileService::IOWorker::Register, - io_worker_, + base::Bind(&GCMService::IOWorker::Register, + base::Unretained(io_worker_.get()), app_id, normalized_sender_ids)); } -void GCMProfileService::Unregister(const std::string& app_id, - UnregisterCallback callback) { +void GCMService::Unregister(const std::string& app_id, + UnregisterCallback callback) { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); - DCHECK(!app_id.empty() && !callback.is_null()); + DCHECK(!app_id.empty()); + DCHECK(!callback.is_null()); GCMClient::Result result = EnsureAppReady(app_id); if (result != GCMClient::SUCCESS) { @@ -623,17 +541,16 @@ void GCMProfileService::Unregister(const std::string& app_id, // 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)); + delayed_task_controller_->AddTask(base::Bind(&GCMService::DoUnregister, + weak_ptr_factory_.GetWeakPtr(), + app_id)); return; } DoUnregister(app_id); } -void GCMProfileService::DoUnregister(const std::string& 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 @@ -642,17 +559,19 @@ void GCMProfileService::DoUnregister(const std::string& app_id) { content::BrowserThread::PostTask( content::BrowserThread::IO, FROM_HERE, - base::Bind(&GCMProfileService::IOWorker::Unregister, - io_worker_, + base::Bind(&GCMService::IOWorker::Unregister, + base::Unretained(io_worker_.get()), app_id)); } -void GCMProfileService::Send(const std::string& app_id, - const std::string& receiver_id, - const GCMClient::OutgoingMessage& message, - SendCallback callback) { +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() && !receiver_id.empty() && !callback.is_null()); + DCHECK(!app_id.empty()); + DCHECK(!receiver_id.empty()); + DCHECK(!callback.is_null()); GCMClient::Result result = EnsureAppReady(app_id); if (result != GCMClient::SUCCESS) { @@ -671,45 +590,48 @@ void GCMProfileService::Send(const std::string& app_id, // 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)); + 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 GCMProfileService::DoSend(const std::string& app_id, - const std::string& receiver_id, - const GCMClient::OutgoingMessage& 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(&GCMProfileService::IOWorker::Send, - io_worker_, + base::Bind(&GCMService::IOWorker::Send, + base::Unretained(io_worker_.get()), app_id, receiver_id, message)); } -GCMClient* GCMProfileService::GetGCMClientForTesting() const { +GCMClient* GCMService::GetGCMClientForTesting() const { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); return io_worker_ ? io_worker_->gcm_client_for_testing() : NULL; } -std::string GCMProfileService::SignedInUserName() const { - return username_; +bool GCMService::IsStarted() const { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + return !account_id_.empty(); } -bool GCMProfileService::IsGCMClientReady() const { +bool GCMService::IsGCMClientReady() const { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); return gcm_client_ready_; } -void GCMProfileService::GetGCMStatistics( - GetGCMStatisticsCallback callback, bool clear_logs) { +void GCMService::GetGCMStatistics(GetGCMStatisticsCallback callback, + bool clear_logs) { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); DCHECK(!callback.is_null()); @@ -717,62 +639,45 @@ void GCMProfileService::GetGCMStatistics( content::BrowserThread::PostTask( content::BrowserThread::IO, FROM_HERE, - base::Bind(&GCMProfileService::IOWorker::GetGCMStatistics, - io_worker_, + base::Bind(&GCMService::IOWorker::GetGCMStatistics, + base::Unretained(io_worker_.get()), clear_logs)); } -void GCMProfileService::SetGCMRecording( - GetGCMStatisticsCallback callback, bool recording) { +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(&GCMProfileService::IOWorker::SetGCMRecording, - io_worker_, + base::Bind(&GCMService::IOWorker::SetGCMRecording, + base::Unretained(io_worker_.get()), 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) +void GCMService::OnActiveAccountLogin() { + if (ShouldStartAutomatically()) EnsureLoaded(); } -void GCMProfileService::GoogleSignedOut(const std::string& username) { +void GCMService::OnActiveAccountLogout() { CheckOut(); } -void GCMProfileService::EnsureLoaded() { - SigninManagerBase* manager = SigninManagerFactory::GetForProfile(profile_); - if (!manager) - return; - std::string username = manager->GetAuthenticatedUsername(); - if (username.empty()) +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 (username_ == username) + if (account_id_ == account_id) return; - username_ = username; + account_id_ = account_id; DCHECK(!delayed_task_controller_); delayed_task_controller_.reset(new DelayedTaskController); @@ -784,24 +689,25 @@ void GCMProfileService::EnsureLoaded() { content::BrowserThread::PostTask( content::BrowserThread::IO, FROM_HERE, - base::Bind(&GCMProfileService::IOWorker::Load, - io_worker_, + base::Bind(&GCMService::IOWorker::Load, + base::Unretained(io_worker_.get()), weak_ptr_factory_.GetWeakPtr())); } -void GCMProfileService::RemoveCachedData() { +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(); - username_.clear(); + account_id_.clear(); gcm_client_ready_ = false; delayed_task_controller_.reset(); register_callbacks_.clear(); send_callbacks_.clear(); } -void GCMProfileService::CheckOut() { +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 @@ -813,36 +719,31 @@ void GCMProfileService::CheckOut() { content::BrowserThread::PostTask( content::BrowserThread::IO, FROM_HERE, - base::Bind(&GCMProfileService::IOWorker::CheckOut, io_worker_)); + base::Bind(&GCMService::IOWorker::CheckOut, + base::Unretained(io_worker_.get()))); } -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) { +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 profile was not signed in, bail out. - if (username_.empty()) + // If the service was not started, bail out. + if (account_id_.empty()) return GCMClient::NOT_SIGNED_IN; return GCMClient::SUCCESS; } -bool GCMProfileService::IsAsyncOperationPending( - const std::string& app_id) const { +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 GCMProfileService::RegisterFinished(const std::string& app_id, - const std::string& registration_id, - GCMClient::Result result) { +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::iterator callback_iter = @@ -857,8 +758,8 @@ void GCMProfileService::RegisterFinished(const std::string& app_id, callback.Run(registration_id, result); } -void GCMProfileService::UnregisterFinished(const std::string& app_id, - GCMClient::Result result) { +void GCMService::UnregisterFinished(const std::string& app_id, + GCMClient::Result result) { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); std::map::iterator callback_iter = @@ -871,9 +772,9 @@ void GCMProfileService::UnregisterFinished(const std::string& app_id, callback.Run(result); } -void GCMProfileService::SendFinished(const std::string& app_id, - const std::string& message_id, - GCMClient::Result 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, SendCallback>::iterator @@ -889,40 +790,40 @@ void GCMProfileService::SendFinished(const std::string& app_id, callback.Run(message_id, result); } -void GCMProfileService::MessageReceived(const std::string& app_id, - GCMClient::IncomingMessage message) { +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 (username_.empty()) + if (account_id_.empty()) return; GetAppHandler(app_id)->OnMessage(app_id, message); } -void GCMProfileService::MessagesDeleted(const std::string& app_id) { +void GCMService::MessagesDeleted(const std::string& app_id) { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); // Drop the event if signed out. - if (username_.empty()) + if (account_id_.empty()) return; GetAppHandler(app_id)->OnMessagesDeleted(app_id); } -void GCMProfileService::MessageSendError( +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 (username_.empty()) + if (account_id_.empty()) return; GetAppHandler(app_id)->OnSendError(app_id, send_error_details); } -void GCMProfileService::GCMClientReady() { +void GCMService::GCMClientReady() { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); if (gcm_client_ready_) @@ -932,13 +833,15 @@ void GCMProfileService::GCMClientReady() { delayed_task_controller_->SetReady(); } -GCMAppHandler* GCMProfileService::GetAppHandler(const std::string& app_id) { +GCMAppHandler* GCMService::GetAppHandler(const std::string& app_id) { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + std::map::const_iterator iter = app_handlers_.find(app_id); return iter == app_handlers_.end() ? &default_app_handler_ : iter->second; } -void GCMProfileService::GetGCMStatisticsFinished( +void GCMService::GetGCMStatisticsFinished( GCMClient::GCMStatistics stats) { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); request_gcm_statistics_callback_.Run(stats); diff --git a/chrome/browser/services/gcm/gcm_profile_service.h b/chrome/browser/services/gcm/gcm_service.h similarity index 62% copy from chrome/browser/services/gcm/gcm_profile_service.h copy to chrome/browser/services/gcm/gcm_service.h index b93bfa2b3066..321be2ea31d1 100644 --- a/chrome/browser/services/gcm/gcm_profile_service.h +++ b/chrome/browser/services/gcm/gcm_service.h @@ -1,50 +1,40 @@ -// 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. -#ifndef CHROME_BROWSER_SERVICES_GCM_GCM_PROFILE_SERVICE_H_ -#define CHROME_BROWSER_SERVICES_GCM_GCM_PROFILE_SERVICE_H_ +#ifndef CHROME_BROWSER_SERVICES_GCM_GCM_SERVICE_H_ +#define CHROME_BROWSER_SERVICES_GCM_GCM_SERVICE_H_ #include #include +#include #include "base/basictypes.h" #include "base/callback.h" #include "base/compiler_specific.h" -#include "base/gtest_prod_util.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 "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/gaia/identity_provider.h" #include "google_apis/gcm/gcm_client.h" -class Profile; - -namespace base { -class Value; -} - namespace extensions { class ExtensionGCMAppHandlerTest; } -namespace user_prefs { -class PrefRegistrySyncable; +namespace net { +class URLRequestContextGetter; } 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 bridge between the GCM users in Chrome and the GCMClient layer. +class GCMService : public IdentityProvider::Observer { public: typedef base::Callback RegisterCallback; @@ -54,29 +44,8 @@ class GCMProfileService : public KeyedService, typedef base::Callback GetGCMStatisticsCallback; - // Any change made to this enum should have corresponding change in the - // GetGCMEnabledStateString(...) function. - enum GCMEnabledState { - // GCM is always enabled. GCMClient will always load and connect with GCM. - ALWAYS_ENABLED, - // GCM is only enabled for apps. GCMClient will start to load and connect - // with GCM only when GCM API is used. - ENABLED_FOR_APPS, - // GCM is always disabled. GCMClient will never load and connect with GCM. - ALWAYS_DISABLED - }; - - // Returns the GCM enabled state. - static GCMEnabledState GetGCMEnabledState(Profile* profile); - - // Returns text representation of a GCMEnabledState enum entry. - static std::string GetGCMEnabledStateString(GCMEnabledState state); - - // Register profile-specific prefs for GCM. - static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry); - - explicit GCMProfileService(Profile* profile); - virtual ~GCMProfileService(); + explicit GCMService(scoped_ptr identity_provider); + virtual ~GCMService(); void Initialize(scoped_ptr gcm_client_factory); @@ -84,14 +53,15 @@ class GCMProfileService : public KeyedService, void Stop(); - // KeyedService implementation. - virtual void Shutdown() OVERRIDE; + // 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. - virtual void AddAppHandler(const std::string& app_id, GCMAppHandler* handler); + void AddAppHandler(const std::string& app_id, GCMAppHandler* handler); // Remove the handler for a given app. - virtual void RemoveAppHandler(const std::string& app_id); + void RemoveAppHandler(const std::string& app_id); // Registers |sender_id| for an app. A registration ID will be returned by // the GCM server. @@ -123,8 +93,8 @@ class GCMProfileService : public KeyedService, // For testing purpose. GCMClient* GetGCMClientForTesting() const; - // Returns the user name if the profile is signed in. - std::string SignedInUserName() const; + // Returns true if the service was started. + bool IsStarted() const; // Returns true if the gcm client is ready. bool IsGCMClientReady() const; @@ -132,14 +102,27 @@ class GCMProfileService : public KeyedService, // 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); + 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 + GetURLRequestContextGetter() const = 0; + + scoped_ptr identity_provider_; + private: - friend class GCMProfileServiceTestConsumer; + friend class TestGCMServiceWrapper; friend class extensions::ExtensionGCMAppHandlerTest; class DelayedTaskController; @@ -147,31 +130,16 @@ class GCMProfileService : public KeyedService, typedef std::map 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. + // 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 when the profile has been signed out. This will erase - // all the cached and persisted data. + // Checks out of GCM and erases any 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); @@ -206,21 +174,18 @@ class GCMProfileService : public KeyedService, 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_; + // The account ID that this service is responsible for. Empty when the service + // is not running. + std::string account_id_; scoped_ptr delayed_task_controller_; - // For all the work occured in IO thread. - scoped_refptr io_worker_; + // For all the work occurring on the IO thread. Must be destroyed on the IO + // thread. + scoped_ptr io_worker_; // App handler map (from app_id to handler pointer). // The handler is not owned. @@ -242,11 +207,11 @@ class GCMProfileService : public KeyedService, GetGCMStatisticsCallback request_gcm_statistics_callback_; // Used to pass a weak pointer to the IO worker. - base::WeakPtrFactory weak_ptr_factory_; + base::WeakPtrFactory weak_ptr_factory_; - DISALLOW_COPY_AND_ASSIGN(GCMProfileService); + DISALLOW_COPY_AND_ASSIGN(GCMService); }; } // namespace gcm -#endif // CHROME_BROWSER_SERVICES_GCM_GCM_PROFILE_SERVICE_H_ +#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 000000000000..b54e8cab2f0c --- /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 ToSenderList(const std::string& sender_ids) { + std::vector 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 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 identity_provider, + const scoped_refptr& request_context); + virtual ~TestGCMService(); + + protected: + // GCMService: + virtual bool ShouldStartAutomatically() const OVERRIDE; + virtual base::FilePath GetStorePath() const OVERRIDE; + virtual scoped_refptr + GetURLRequestContextGetter() const OVERRIDE; + + private: + base::ScopedTempDir temp_dir_; + scoped_refptr 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 identity_provider, + const scoped_refptr& 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 +TestGCMService::GetURLRequestContextGetter() const { + return request_context_; +} + +} // namespace + +class TestGCMServiceWrapper { + public: + enum WaitToFinish { + DO_NOT_WAIT, + WAIT + }; + + explicit TestGCMServiceWrapper( + const scoped_refptr& 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& 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 request_context_; + FakeOAuth2TokenService token_service_; + scoped_ptr identity_provider_owner_; + FakeIdentityProvider* identity_provider_; + scoped_ptr service_; + scoped_ptr 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& 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(service_->GetGCMClientForTesting()); +} + +void TestGCMServiceWrapper::CreateService( + bool start_automatically, + GCMClientMock::LoadingDelay gcm_client_loading_delay) { + service_.reset(new TestGCMService( + start_automatically, + identity_provider_owner_.PassAs(), + request_context_)); + service_->Initialize(scoped_ptr( + 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& 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 thread_bundle_; + scoped_refptr request_context_; + scoped_ptr 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 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 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 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 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 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 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 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 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 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 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 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 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 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 sender_ids; + sender_ids.push_back("sender1"); + wrapper_->Register(kTestAppID1, sender_ids, TestGCMServiceWrapper::WAIT); + + // Register the same app in a different instance. + std::vector 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 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 sender_ids; + sender_ids.push_back("sender1"); + wrapper_->Register(kTestAppID1, sender_ids, TestGCMServiceWrapper::WAIT); + + // Register the same app in a different instance. + std::vector 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 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 7ae1a4699c0d..2a9299353b7b 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 4567f42caf37..6ee7fa8d4e9c 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 d1d076678481..b4af0a49c79c 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 b2b8d6cc323c..3f2afc903d61 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 ff371ebef4f8..36485a19293c 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; -- 2.11.4.GIT