Commit 3ecb2b58 authored by jianli@chromium.org's avatar jianli@chromium.org

Start and stop the GCM service on demand

The GCM service will now be started when there is a consumer. It will be
automatically stopped when all consumers are gone. This does not change
the existing behavior that the GCM is always started for the signed-in
profile since invalidation consumes it.

BUG=356716
TEST=tests updated and new tests added
TBR=kalman@chromium.org

Review URL: https://codereview.chromium.org/296053011

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@272380 0039d316-1c4b-4281-b951-d872f2087c98
parent f916f7c8
......@@ -20,6 +20,7 @@
#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_app_handler.h"
#include "chrome/browser/services/gcm/fake_gcm_client.h"
#include "chrome/browser/services/gcm/fake_gcm_client_factory.h"
#include "chrome/browser/services/gcm/fake_signin_manager.h"
......@@ -381,11 +382,19 @@ TEST_F(ExtensionGCMAppHandlerTest, UnregisterOnExtensionUninstall) {
waiter()->WaitUntilCompleted();
EXPECT_EQ(gcm::GCMClient::SUCCESS, registration_result());
// Add another app handler in order to prevent the GCM service from being
// stopped when the extension is uninstalled. This is needed because otherwise
// we are not able to receive the unregistration result.
GetGCMDriver()->AddAppHandler("Foo", gcm_app_handler());
// Unregistration should be triggered when the extension is uninstalled.
UninstallExtension(extension);
waiter()->WaitUntilCompleted();
EXPECT_EQ(gcm::GCMClient::SUCCESS,
gcm_app_handler()->unregistration_result());
// Clean up.
GetGCMDriver()->RemoveAppHandler("Foo");
}
} // namespace extensions
// 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_app_handler.h"
#include "base/run_loop.h"
namespace gcm {
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();
}
} // 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.
#ifndef CHROME_BROWSER_SERVICES_GCM_FAKE_GCM_APP_HANDLER_H_
#define CHROME_BROWSER_SERVICES_GCM_FAKE_GCM_APP_HANDLER_H_
#include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h"
#include "components/gcm_driver/gcm_app_handler.h"
namespace base {
class RunLoop;
}
namespace gcm {
class FakeGCMAppHandler : public GCMAppHandler {
public:
enum Event {
NO_EVENT,
MESSAGE_EVENT,
MESSAGES_DELETED_EVENT,
SEND_ERROR_EVENT
};
FakeGCMAppHandler();
virtual ~FakeGCMAppHandler();
const Event& received_event() const { return received_event_; }
const std::string& app_id() const { return app_id_; }
const GCMClient::IncomingMessage& message() const { return message_; }
const GCMClient::SendErrorDetails& send_error_details() const {
return send_error_details_;
}
void WaitForNotification();
// GCMAppHandler:
virtual void ShutdownHandler() OVERRIDE;
virtual void OnMessage(const std::string& app_id,
const GCMClient::IncomingMessage& message) OVERRIDE;
virtual void OnMessagesDeleted(const std::string& app_id) OVERRIDE;
virtual void OnSendError(
const std::string& app_id,
const GCMClient::SendErrorDetails& send_error_details) OVERRIDE;
private:
void ClearResults();
scoped_ptr<base::RunLoop> run_loop_;
Event received_event_;
std::string app_id_;
GCMClient::IncomingMessage message_;
GCMClient::SendErrorDetails send_error_details_;
DISALLOW_COPY_AND_ASSIGN(FakeGCMAppHandler);
};
} // namespace gcm
#endif // CHROME_BROWSER_SERVICES_GCM_FAKE_GCM_APP_HANDLER_H_
......@@ -475,6 +475,10 @@ void GCMDriver::RemoveAppHandler(const std::string& app_id) {
DCHECK(!app_id.empty());
app_handlers_.erase(app_id);
// Stops the GCM service when no app intends to consume it.
if (app_handlers_.empty())
Stop();
}
void GCMDriver::Register(const std::string& app_id,
......@@ -681,6 +685,10 @@ GCMClient::Result GCMDriver::EnsureStarted() {
if (!gcm_enabled_)
return GCMClient::GCM_DISABLED;
// Have any app requested the service?
if (app_handlers_.empty())
return GCMClient::UNKNOWN_ERROR;
// Is the user signed in?
const std::string account_id = identity_provider_->GetActiveAccountId();
if (account_id.empty())
......
......@@ -14,6 +14,7 @@
#include "base/strings/string_util.h"
#include "base/test/test_simple_task_runner.h"
#include "base/threading/thread.h"
#include "chrome/browser/services/gcm/fake_gcm_app_handler.h"
#include "chrome/browser/services/gcm/fake_gcm_client.h"
#include "chrome/browser/services/gcm/fake_gcm_client_factory.h"
#include "chrome/browser/services/gcm/gcm_app_handler.h"
......@@ -51,100 +52,6 @@ std::vector<std::string> ToSenderList(const std::string& sender_ids) {
return senders;
}
class FakeGCMAppHandler : public GCMAppHandler {
public:
enum Event {
NO_EVENT,
MESSAGE_EVENT,
MESSAGES_DELETED_EVENT,
SEND_ERROR_EVENT
};
FakeGCMAppHandler();
virtual ~FakeGCMAppHandler();
const Event& received_event() const { return received_event_; }
const std::string& app_id() const { return app_id_; }
const GCMClient::IncomingMessage& message() const { return message_; }
const GCMClient::SendErrorDetails& send_error_details() const {
return send_error_details_;
}
void WaitForNotification();
// GCMAppHandler:
virtual void ShutdownHandler() OVERRIDE;
virtual void OnMessage(const std::string& app_id,
const GCMClient::IncomingMessage& message) OVERRIDE;
virtual void OnMessagesDeleted(const std::string& app_id) OVERRIDE;
virtual void OnSendError(
const std::string& app_id,
const GCMClient::SendErrorDetails& send_error_details) OVERRIDE;
private:
void ClearResults();
scoped_ptr<base::RunLoop> run_loop_;
Event received_event_;
std::string app_id_;
GCMClient::IncomingMessage message_;
GCMClient::SendErrorDetails send_error_details_;
DISALLOW_COPY_AND_ASSIGN(FakeGCMAppHandler);
};
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();
}
} // namespace
class GCMDriverTest : public testing::Test {
......@@ -179,6 +86,8 @@ class GCMDriverTest : public testing::Test {
FakeGCMClient* GetGCMClient();
void CreateDriver(FakeGCMClient::StartMode gcm_client_start_mode);
void AddAppHandlers();
void RemoveAppHandlers();
void SignIn(const std::string& account_id);
void SignOut();
......@@ -295,10 +204,18 @@ void GCMDriverTest::CreateDriver(
task_runner_));
gcm_app_handler_.reset(new FakeGCMAppHandler);
}
void GCMDriverTest::AddAppHandlers() {
driver_->AddAppHandler(kTestAppID1, gcm_app_handler_.get());
driver_->AddAppHandler(kTestAppID2, gcm_app_handler_.get());
}
void GCMDriverTest::RemoveAppHandlers() {
driver_->RemoveAppHandler(kTestAppID1);
driver_->RemoveAppHandler(kTestAppID2);
}
void GCMDriverTest::SignIn(const std::string& account_id) {
token_service_.AddAccount(account_id);
identity_provider_->LogIn(account_id);
......@@ -380,71 +297,103 @@ void GCMDriverTest::UnregisterCompleted(GCMClient::Result result) {
}
TEST_F(GCMDriverTest, CreateGCMDriverBeforeSignIn) {
// Create CreateGMCService first.
// Create GCMDriver first. GCM is not started.
CreateDriver(FakeGCMClient::NO_DELAY_START);
EXPECT_FALSE(driver()->IsStarted());
// Sign in. This will kick off the check-in.
// Sign in. GCM is still not started.
SignIn(kTestAccountID1);
EXPECT_FALSE(driver()->IsStarted());
// GCM will be started only after both sign-in and app handler being
AddAppHandlers();
EXPECT_TRUE(driver()->IsStarted());
}
TEST_F(GCMDriverTest, CreateGCMDriverAfterSignIn) {
// Sign in. This will not initiate the check-in.
// Sign in. Nothings happens since GCMDriver is not created.
SignIn(kTestAccountID1);
// Create GCMeService after sign-in.
// Create GCMDriver after sign-in. GCM is not started.
CreateDriver(FakeGCMClient::NO_DELAY_START);
EXPECT_FALSE(driver()->IsStarted());
// GCM will be started only after both sign-in and app handler being
AddAppHandlers();
EXPECT_TRUE(driver()->IsStarted());
}
TEST_F(GCMDriverTest, Shutdown) {
CreateDriver(FakeGCMClient::NO_DELAY_START);
EXPECT_FALSE(HasAppHandlers());
AddAppHandlers();
EXPECT_TRUE(HasAppHandlers());
driver()->Shutdown();
EXPECT_FALSE(HasAppHandlers());
}
TEST_F(GCMDriverTest, SignInAndSignOutUnderPositiveChannelSignal) {
TEST_F(GCMDriverTest, SignInAndSignOutOnGCMEnabled) {
// By default, GCM is enabled.
CreateDriver(FakeGCMClient::NO_DELAY_START);
SignIn(kTestAccountID1);
AddAppHandlers();
// GCMClient should be loaded.
// GCMClient should be started after sign-in.
SignIn(kTestAccountID1);
EXPECT_TRUE(driver()->IsGCMClientReady());
EXPECT_EQ(FakeGCMClient::STARTED, GetGCMClient()->status());
// GCMClient should be checked out after sign-out.
SignOut();
EXPECT_FALSE(driver()->IsGCMClientReady());
EXPECT_EQ(FakeGCMClient::CHECKED_OUT, GetGCMClient()->status());
}
// GCMClient should be checked out.
TEST_F(GCMDriverTest, SignInAndSignOutOnGCMDisabled) {
// By default, GCM is enabled.
CreateDriver(FakeGCMClient::NO_DELAY_START);
AddAppHandlers();
// Disable GCM.
driver()->Disable();
// GCMClient should not be started after sign-in.
SignIn(kTestAccountID1);
EXPECT_FALSE(driver()->IsGCMClientReady());
EXPECT_EQ(FakeGCMClient::UNINITIALIZED, GetGCMClient()->status());
// Check-out should still be performed after sign-out.
SignOut();
EXPECT_FALSE(driver()->IsGCMClientReady());
EXPECT_EQ(FakeGCMClient::CHECKED_OUT, GetGCMClient()->status());
}
TEST_F(GCMDriverTest, SignOutAndThenSignIn) {
CreateDriver(FakeGCMClient::NO_DELAY_START);
SignIn(kTestAccountID1);
AddAppHandlers();
// GCMClient should be loaded.
// GCMClient should be started after sign-in.
SignIn(kTestAccountID1);
EXPECT_TRUE(driver()->IsGCMClientReady());
EXPECT_EQ(FakeGCMClient::STARTED, GetGCMClient()->status());
// GCMClient should be checked out after sign-out.
SignOut();
// GCMClient should be checked out.
EXPECT_FALSE(driver()->IsGCMClientReady());
EXPECT_EQ(FakeGCMClient::CHECKED_OUT, GetGCMClient()->status());
// Sign-in with a different account.
SignIn(kTestAccountID2);
// GCMClient should be loaded again.
// GCMClient should be started again.
EXPECT_TRUE(driver()->IsGCMClientReady());
EXPECT_EQ(FakeGCMClient::STARTED, GetGCMClient()->status());
}
TEST_F(GCMDriverTest, DisableAndReenableGCM) {
CreateDriver(FakeGCMClient::NO_DELAY_START);
AddAppHandlers();
SignIn(kTestAccountID1);
// GCMClient should be started.
......@@ -486,6 +435,50 @@ TEST_F(GCMDriverTest, DisableAndReenableGCM) {
EXPECT_EQ(FakeGCMClient::CHECKED_OUT, GetGCMClient()->status());
}
TEST_F(GCMDriverTest, StartOrStopGCMOnDemand) {
CreateDriver(FakeGCMClient::NO_DELAY_START);
SignIn(kTestAccountID1);
// GCMClient is not started.
EXPECT_FALSE(driver()->IsGCMClientReady());
EXPECT_EQ(FakeGCMClient::UNINITIALIZED, GetGCMClient()->status());
// GCMClient is started after an app handler has been added.
driver()->AddAppHandler(kTestAppID1, gcm_app_handler());
PumpIOLoop();
PumpUILoop();
EXPECT_TRUE(driver()->IsGCMClientReady());
EXPECT_EQ(FakeGCMClient::STARTED, GetGCMClient()->status());
// Add another app handler.
driver()->AddAppHandler(kTestAppID2, gcm_app_handler());
PumpIOLoop();
PumpUILoop();
EXPECT_TRUE(driver()->IsGCMClientReady());
EXPECT_EQ(FakeGCMClient::STARTED, GetGCMClient()->status());
// GCMClient remains active after one app handler is gone.
driver()->RemoveAppHandler(kTestAppID1);
PumpIOLoop();
PumpUILoop();
EXPECT_TRUE(driver()->IsGCMClientReady());
EXPECT_EQ(FakeGCMClient::STARTED, GetGCMClient()->status());
// GCMClient should be stopped after the last app handler is gone.
driver()->RemoveAppHandler(kTestAppID2);
PumpIOLoop();
PumpUILoop();
EXPECT_FALSE(driver()->IsGCMClientReady());
EXPECT_EQ(FakeGCMClient::STOPPED, GetGCMClient()->status());
// GCMClient is restarted after an app handler has been added.
driver()->AddAppHandler(kTestAppID2, gcm_app_handler());
PumpIOLoop();
PumpUILoop();
EXPECT_TRUE(driver()->IsGCMClientReady());
EXPECT_EQ(FakeGCMClient::STARTED, GetGCMClient()->status());
}
TEST_F(GCMDriverTest, RegisterFailed) {
std::vector<std::string> sender_ids;
sender_ids.push_back("sender1");
......@@ -502,9 +495,19 @@ TEST_F(GCMDriverTest, RegisterFailed) {
// Registration fails when the sign-in does not occur.
driver()->Enable();
AddAppHandlers();
Register(kTestAppID1, sender_ids, GCMDriverTest::WAIT);
EXPECT_TRUE(registration_id().empty());
EXPECT_EQ(GCMClient::NOT_SIGNED_IN, registration_result());
ClearResults();
// Registration fails when the no app handler is added.
RemoveAppHandlers();
SignIn(kTestAccountID1);
Register(kTestAppID1, sender_ids, GCMDriverTest::WAIT);
EXPECT_TRUE(registration_id().empty());
EXPECT_EQ(GCMClient::UNKNOWN_ERROR, registration_result());
}
TEST_F(GCMDriverTest, UnregisterFailed) {
......@@ -519,8 +522,17 @@ TEST_F(GCMDriverTest, UnregisterFailed) {
// Unregistration fails when the sign-in does not occur.
driver()->Enable();
AddAppHandlers();
Unregister(kTestAppID1, GCMDriverTest::WAIT);
EXPECT_EQ(GCMClient::NOT_SIGNED_IN, unregistration_result());
ClearResults();
// Unregistration fails when the no app handler is added.
RemoveAppHandlers();
SignIn(kTestAccountID1);
Unregister(kTestAppID1, GCMDriverTest::WAIT);
EXPECT_EQ(GCMClient::UNKNOWN_ERROR, unregistration_result());
}
TEST_F(GCMDriverTest, SendFailed) {
......@@ -538,17 +550,28 @@ TEST_F(GCMDriverTest, SendFailed) {
ClearResults();
// Registration fails when the sign-in does not occur.
// Sending fails when the sign-in does not occur.
driver()->Enable();
AddAppHandlers();
Send(kTestAppID1, kUserID1, message, GCMDriverTest::WAIT);
EXPECT_TRUE(send_message_id().empty());
EXPECT_EQ(GCMClient::NOT_SIGNED_IN, send_result());
ClearResults();
// Sending fails when the no app handler is added.
RemoveAppHandlers();
SignIn(kTestAccountID1);
Send(kTestAppID1, kUserID1, message, GCMDriverTest::WAIT);
EXPECT_TRUE(send_message_id().empty());
EXPECT_EQ(GCMClient::UNKNOWN_ERROR, send_result());
}
TEST_F(GCMDriverTest, GCMClientNotReadyBeforeRegistration) {
// Make GCMClient not ready initially.
CreateDriver(FakeGCMClient::DELAY_START);
SignIn(kTestAccountID1);
AddAppHandlers();
// The registration is on hold until GCMClient is ready.
std::vector<std::string> sender_ids;
......@@ -572,6 +595,7 @@ TEST_F(GCMDriverTest, GCMClientNotReadyBeforeSending) {
// Make GCMClient not ready initially.
CreateDriver(FakeGCMClient::DELAY_START);
SignIn(kTestAccountID1);
AddAppHandlers();
// The sending is on hold until GCMClient is ready.
GCMClient::OutgoingMessage message;
......@@ -615,6 +639,7 @@ void GCMDriverFunctionalTest::SetUp() {
GCMDriverTest::SetUp();
CreateDriver(FakeGCMClient::NO_DELAY_START);
AddAppHandlers();
SignIn(kTestAccountID1);
}
......
......@@ -11,6 +11,7 @@
#include "base/callback.h"
#include "base/memory/scoped_ptr.h"
#include "base/run_loop.h"
#include "chrome/browser/services/gcm/fake_gcm_app_handler.h"
#include "chrome/browser/services/gcm/fake_gcm_client.h"
#include "chrome/browser/services/gcm/fake_gcm_client_factory.h"
#include "chrome/browser/services/gcm/fake_signin_manager.h"
......@@ -54,6 +55,7 @@ class GCMProfileServiceTest : public testing::Test {
// testing::Test:
virtual void SetUp() OVERRIDE;
virtual void TearDown() OVERRIDE;
FakeGCMClient* GetGCMClient() const;
......@@ -83,6 +85,7 @@ class GCMProfileServiceTest : public testing::Test {
content::TestBrowserThreadBundle thread_bundle_;
scoped_ptr<TestingProfile> profile_;
GCMProfileService* gcm_profile_service_;
scoped_ptr<FakeGCMAppHandler> gcm_app_handler_;
std::string registration_id_;
GCMClient::Result registration_result_;
......@@ -95,6 +98,7 @@ class GCMProfileServiceTest : public testing::Test {
GCMProfileServiceTest::GCMProfileServiceTest()
: gcm_profile_service_(NULL),
gcm_app_handler_(new FakeGCMAppHandler),
registration_result_(GCMClient::UNKNOWN_ERROR),
send_result_(GCMClient::UNKNOWN_ERROR) {
}
......@@ -117,6 +121,8 @@ void GCMProfileServiceTest::SetUp() {
GCMProfileServiceFactory::GetInstance()->SetTestingFactoryAndUse(
profile_.get(),
&BuildGCMProfileService));
gcm_profile_service_->driver()->AddAppHandler(
kTestAppID, gcm_app_handler_.get());
FakeSigninManager* signin_manager = static_cast<FakeSigninManager*>(
SigninManagerFactory::GetInstance()->GetForProfile(profile_.get()));
......@@ -124,6 +130,10 @@ void GCMProfileServiceTest::SetUp() {
base::RunLoop().RunUntilIdle();
}
void GCMProfileServiceTest::TearDown() {
gcm_profile_service_->driver()->RemoveAppHandler(kTestAppID);
}
void GCMProfileServiceTest::RegisterAndWaitForCompletion(
const std::vector<std::string>& sender_ids) {
base::RunLoop run_loop;
......
......@@ -1266,6 +1266,8 @@
'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_app_handler.cc',
'browser/services/gcm/fake_gcm_app_handler.h',
'browser/services/gcm/fake_gcm_client_factory.cc',
'browser/services/gcm/fake_gcm_client_factory.h',
'browser/services/gcm/fake_signin_manager.cc',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment