summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/browser_process_impl.cc1
-rw-r--r--chrome/browser/prefs/browser_prefs.cc4
-rw-r--r--chrome/browser/services/gcm/gcm_desktop_utils.cc2
-rw-r--r--chrome/browser/services/gcm/gcm_desktop_utils.h2
-rw-r--r--chrome/browser/services/gcm/gcm_profile_service.cc1
-rw-r--r--components/gcm_driver.gypi4
-rw-r--r--components/gcm_driver/BUILD.gn4
-rw-r--r--components/gcm_driver/DEPS1
-rw-r--r--components/gcm_driver/gcm_channel_status_request.cc4
-rw-r--r--components/gcm_driver/gcm_channel_status_request.h5
-rw-r--r--components/gcm_driver/gcm_channel_status_request_unittest.cc15
-rw-r--r--components/gcm_driver/gcm_channel_status_syncer.cc183
-rw-r--r--components/gcm_driver/gcm_channel_status_syncer.h98
-rw-r--r--components/gcm_driver/gcm_driver_desktop.cc14
-rw-r--r--components/gcm_driver/gcm_driver_desktop.h13
-rw-r--r--components/gcm_driver/gcm_driver_desktop_unittest.cc270
16 files changed, 600 insertions, 21 deletions
diff --git a/chrome/browser/browser_process_impl.cc b/chrome/browser/browser_process_impl.cc
index 03f9702..36de9d9 100644
--- a/chrome/browser/browser_process_impl.cc
+++ b/chrome/browser/browser_process_impl.cc
@@ -1135,6 +1135,7 @@ void BrowserProcessImpl::CreateGCMDriver() {
CHECK(PathService::Get(chrome::DIR_GLOBAL_GCM_STORE, &store_path));
gcm_driver_ = gcm::CreateGCMDriverDesktop(
make_scoped_ptr(new gcm::GCMClientFactory),
+ local_state(),
store_path,
system_request_context());
// Sign-in is not required for device-level GCM usage. So we just call
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 3d27e95..4992da8 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -82,6 +82,7 @@
#include "components/autofill/core/browser/autofill_manager.h"
#include "components/bookmarks/browser/bookmark_utils.h"
#include "components/dom_distiller/core/distilled_page_prefs.h"
+#include "components/gcm_driver/gcm_channel_status_syncer.h"
#include "components/google/core/browser/google_pref_names.h"
#include "components/google/core/browser/google_url_tracker.h"
#include "components/network_time/network_time_tracker.h"
@@ -293,6 +294,8 @@ void RegisterLocalState(PrefRegistrySimple* registry) {
AutomaticProfileResetterFactory::RegisterPrefs(registry);
BackgroundModeManager::RegisterPrefs(registry);
RegisterBrowserPrefs(registry);
+ // The native GCM is used on Android instead.
+ gcm::GCMChannelStatusSyncer::RegisterPrefs(registry);
#if !defined(OS_CHROMEOS)
RegisterDefaultBrowserPromptPrefs(registry);
#endif // !defined(OS_CHROMEOS)
@@ -443,6 +446,7 @@ void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) {
extensions::ExtensionSettingsHandler::RegisterProfilePrefs(registry);
extensions::TabsCaptureVisibleTabFunction::RegisterProfilePrefs(registry);
first_run::RegisterProfilePrefs(registry);
+ gcm::GCMChannelStatusSyncer::RegisterProfilePrefs(registry);
NewTabUI::RegisterProfilePrefs(registry);
PepperFlashSettingsManager::RegisterProfilePrefs(registry);
PinnedTabCodec::RegisterProfilePrefs(registry);
diff --git a/chrome/browser/services/gcm/gcm_desktop_utils.cc b/chrome/browser/services/gcm/gcm_desktop_utils.cc
index 7e81c8d..9de629b 100644
--- a/chrome/browser/services/gcm/gcm_desktop_utils.cc
+++ b/chrome/browser/services/gcm/gcm_desktop_utils.cc
@@ -73,6 +73,7 @@ GCMClient::ChromeBuildInfo GetChromeBuildInfo() {
scoped_ptr<GCMDriver> CreateGCMDriverDesktop(
scoped_ptr<GCMClientFactory> gcm_client_factory,
+ PrefService* prefs,
const base::FilePath& store_path,
const scoped_refptr<net::URLRequestContextGetter>& request_context) {
scoped_refptr<base::SequencedWorkerPool> worker_pool(
@@ -84,6 +85,7 @@ scoped_ptr<GCMDriver> CreateGCMDriverDesktop(
return scoped_ptr<GCMDriver>(new GCMDriverDesktop(
gcm_client_factory.Pass(),
GetChromeBuildInfo(),
+ prefs,
store_path,
request_context,
content::BrowserThread::GetMessageLoopProxyForThread(
diff --git a/chrome/browser/services/gcm/gcm_desktop_utils.h b/chrome/browser/services/gcm/gcm_desktop_utils.h
index c882005..8105829 100644
--- a/chrome/browser/services/gcm/gcm_desktop_utils.h
+++ b/chrome/browser/services/gcm/gcm_desktop_utils.h
@@ -8,6 +8,7 @@
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
+class PrefService;
namespace base {
class FilePath;
}
@@ -23,6 +24,7 @@ class GCMClientFactory;
scoped_ptr<GCMDriver> CreateGCMDriverDesktop(
scoped_ptr<GCMClientFactory> gcm_client_factory,
+ PrefService* prefs,
const base::FilePath& store_path,
const scoped_refptr<net::URLRequestContextGetter>& request_context);
diff --git a/chrome/browser/services/gcm/gcm_profile_service.cc b/chrome/browser/services/gcm/gcm_profile_service.cc
index 5324ffd..75e1bdd 100644
--- a/chrome/browser/services/gcm/gcm_profile_service.cc
+++ b/chrome/browser/services/gcm/gcm_profile_service.cc
@@ -169,6 +169,7 @@ GCMProfileService::GCMProfileService(
driver_ = CreateGCMDriverDesktop(
gcm_client_factory.Pass(),
+ profile_->GetPrefs(),
profile_->GetPath().Append(chrome::kGCMStoreDirname),
profile_->GetRequestContext());
diff --git a/components/gcm_driver.gypi b/components/gcm_driver.gypi
index c7531a9..fb6d958 100644
--- a/components/gcm_driver.gypi
+++ b/components/gcm_driver.gypi
@@ -33,6 +33,8 @@
'gcm_driver/gcm_backoff_policy.h',
'gcm_driver/gcm_channel_status_request.cc',
'gcm_driver/gcm_channel_status_request.h',
+ 'gcm_driver/gcm_channel_status_syncer.cc',
+ 'gcm_driver/gcm_channel_status_syncer.h',
'gcm_driver/gcm_client.cc',
'gcm_driver/gcm_client.h',
'gcm_driver/gcm_client_factory.cc',
@@ -73,6 +75,8 @@
'gcm_driver/gcm_account_mapper.h',
'gcm_driver/gcm_channel_status_request.cc',
'gcm_driver/gcm_channel_status_request.h',
+ 'gcm_driver/gcm_channel_status_syncer.cc',
+ 'gcm_driver/gcm_channel_status_syncer.h',
'gcm_driver/gcm_client_factory.cc',
'gcm_driver/gcm_client_factory.h',
'gcm_driver/gcm_client_impl.cc',
diff --git a/components/gcm_driver/BUILD.gn b/components/gcm_driver/BUILD.gn
index a446c9f..9e4f3d7 100644
--- a/components/gcm_driver/BUILD.gn
+++ b/components/gcm_driver/BUILD.gn
@@ -17,6 +17,8 @@ static_library("gcm_driver") {
"gcm_backoff_policy.h",
"gcm_channel_status_request.cc",
"gcm_channel_status_request.h",
+ "gcm_channel_status_syncer.cc",
+ "gcm_channel_status_syncer.h",
"gcm_client.cc",
"gcm_client.h",
"gcm_client_factory.cc",
@@ -51,6 +53,8 @@ static_library("gcm_driver") {
sources -= [
"gcm_channel_status_request.cc",
"gcm_channel_status_request.h",
+ "gcm_channel_status_syncer.cc",
+ "gcm_channel_status_syncer.h",
"gcm_client_factory.cc",
"gcm_client_factory.h",
"gcm_client_impl.cc",
diff --git a/components/gcm_driver/DEPS b/components/gcm_driver/DEPS
index cbffdad..93923ff 100644
--- a/components/gcm_driver/DEPS
+++ b/components/gcm_driver/DEPS
@@ -1,5 +1,6 @@
include_rules = [
"+components/os_crypt",
+ "+components/pref_registry",
# TODO(johnme): Fix this layering violation.
"!content/public/android/java",
"+google_apis/gaia",
diff --git a/components/gcm_driver/gcm_channel_status_request.cc b/components/gcm_driver/gcm_channel_status_request.cc
index 206ee39..fd405d1 100644
--- a/components/gcm_driver/gcm_channel_status_request.cc
+++ b/components/gcm_driver/gcm_channel_status_request.cc
@@ -41,12 +41,12 @@ GCMChannelStatusRequest::~GCMChannelStatusRequest() {
}
// static
-int GCMChannelStatusRequest::default_poll_interval_seconds_for_testing() {
+int GCMChannelStatusRequest::default_poll_interval_seconds() {
return kDefaultPollIntervalSeconds;
}
// static
-int GCMChannelStatusRequest::min_poll_interval_seconds_for_testing() {
+int GCMChannelStatusRequest::min_poll_interval_seconds() {
return kMinPollIntervalSeconds;
}
diff --git a/components/gcm_driver/gcm_channel_status_request.h b/components/gcm_driver/gcm_channel_status_request.h
index b2725759..6ce1bab 100644
--- a/components/gcm_driver/gcm_channel_status_request.h
+++ b/components/gcm_driver/gcm_channel_status_request.h
@@ -36,9 +36,8 @@ class GCMChannelStatusRequest : public net::URLFetcherDelegate {
void Start();
- // Exposed for testing purpose.
- static int default_poll_interval_seconds_for_testing();
- static int min_poll_interval_seconds_for_testing();
+ static int default_poll_interval_seconds();
+ static int min_poll_interval_seconds();
private:
// Overridden from URLFetcherDelegate:
diff --git a/components/gcm_driver/gcm_channel_status_request_unittest.cc b/components/gcm_driver/gcm_channel_status_request_unittest.cc
index daf8122..961ca4e 100644
--- a/components/gcm_driver/gcm_channel_status_request_unittest.cc
+++ b/components/gcm_driver/gcm_channel_status_request_unittest.cc
@@ -136,7 +136,7 @@ TEST_F(GCMChannelStatusRequestTest, ResponseWithDisabledStatus) {
EXPECT_TRUE(request_callback_invoked_);
EXPECT_FALSE(enabled_);
EXPECT_EQ(
- GCMChannelStatusRequest::default_poll_interval_seconds_for_testing(),
+ GCMChannelStatusRequest::default_poll_interval_seconds(),
poll_interval_seconds_);
}
@@ -148,7 +148,7 @@ TEST_F(GCMChannelStatusRequestTest, ResponseWithEnabledStatus) {
EXPECT_TRUE(request_callback_invoked_);
EXPECT_TRUE(enabled_);
EXPECT_EQ(
- GCMChannelStatusRequest::default_poll_interval_seconds_for_testing(),
+ GCMChannelStatusRequest::default_poll_interval_seconds(),
poll_interval_seconds_);
}
@@ -156,8 +156,7 @@ TEST_F(GCMChannelStatusRequestTest, ResponseWithPollInterval) {
// Setting a poll interval 15 minutes longer than the minimum interval we
// enforce.
int poll_interval_seconds =
- GCMChannelStatusRequest::min_poll_interval_seconds_for_testing() +
- 15 * 60;
+ GCMChannelStatusRequest::min_poll_interval_seconds() + 15 * 60;
StartRequest();
SetResponseProtoData(NOT_SPECIFIED, poll_interval_seconds);
CompleteFetch();
@@ -171,22 +170,20 @@ TEST_F(GCMChannelStatusRequestTest, ResponseWithShortPollInterval) {
// Setting a poll interval 15 minutes shorter than the minimum interval we
// enforce.
int poll_interval_seconds =
- GCMChannelStatusRequest::min_poll_interval_seconds_for_testing() -
- 15 * 60;
+ GCMChannelStatusRequest::min_poll_interval_seconds() - 15 * 60;
StartRequest();
SetResponseProtoData(NOT_SPECIFIED, poll_interval_seconds);
CompleteFetch();
EXPECT_TRUE(request_callback_invoked_);
EXPECT_TRUE(enabled_);
- EXPECT_EQ(GCMChannelStatusRequest::min_poll_interval_seconds_for_testing(),
+ EXPECT_EQ(GCMChannelStatusRequest::min_poll_interval_seconds(),
poll_interval_seconds_);
}
TEST_F(GCMChannelStatusRequestTest, ResponseWithDisabledStatusAndPollInterval) {
int poll_interval_seconds =
- GCMChannelStatusRequest::min_poll_interval_seconds_for_testing() +
- 15 * 60;
+ GCMChannelStatusRequest::min_poll_interval_seconds() + 15 * 60;
StartRequest();
SetResponseProtoData(GCM_DISABLED, poll_interval_seconds);
CompleteFetch();
diff --git a/components/gcm_driver/gcm_channel_status_syncer.cc b/components/gcm_driver/gcm_channel_status_syncer.cc
new file mode 100644
index 0000000..7006721
--- /dev/null
+++ b/components/gcm_driver/gcm_channel_status_syncer.cc
@@ -0,0 +1,183 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/gcm_driver/gcm_channel_status_syncer.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "base/prefs/pref_registry_simple.h"
+#include "base/prefs/pref_service.h"
+#include "base/rand_util.h"
+#include "components/gcm_driver/gcm_channel_status_request.h"
+#include "components/gcm_driver/gcm_driver.h"
+#include "components/pref_registry/pref_registry_syncable.h"
+
+namespace gcm {
+
+namespace {
+
+// The GCM channel's enabled state.
+const char kGCMChannelStatus[] = "gcm.channel_status";
+
+// The GCM channel's polling interval (in seconds).
+const char kGCMChannelPollIntervalSeconds[] = "gcm.poll_interval";
+
+// Last time when checking with the GCM channel status server is done.
+const char kGCMChannelLastCheckTime[] = "gcm.check_time";
+
+// A small delay to avoid sending request at browser startup time for first-time
+// request.
+const int kFirstTimeDelaySeconds = 1 * 60; // 1 minute.
+
+// The fuzzing variation added to the polling delay.
+const int kGCMChannelRequestTimeJitterSeconds = 15 * 60; // 15 minues.
+
+} // namespace
+
+// static
+void GCMChannelStatusSyncer::RegisterPrefs(PrefRegistrySimple* registry) {
+ registry->RegisterBooleanPref(kGCMChannelStatus, true);
+ registry->RegisterIntegerPref(
+ kGCMChannelPollIntervalSeconds,
+ GCMChannelStatusRequest::default_poll_interval_seconds());
+ registry->RegisterInt64Pref(kGCMChannelLastCheckTime, 0);
+}
+
+// static
+void GCMChannelStatusSyncer::RegisterProfilePrefs(
+ user_prefs::PrefRegistrySyncable* registry) {
+ registry->RegisterBooleanPref(
+ kGCMChannelStatus,
+ true,
+ user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
+ registry->RegisterIntegerPref(
+ kGCMChannelPollIntervalSeconds,
+ GCMChannelStatusRequest::default_poll_interval_seconds(),
+ user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
+ registry->RegisterInt64Pref(
+ kGCMChannelLastCheckTime,
+ 0,
+ user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
+}
+
+// static
+int GCMChannelStatusSyncer::first_time_delay_seconds() {
+ return kFirstTimeDelaySeconds;
+}
+
+GCMChannelStatusSyncer::GCMChannelStatusSyncer(
+ GCMDriver* driver,
+ PrefService* prefs,
+ const scoped_refptr<net::URLRequestContextGetter>& request_context)
+ : driver_(driver),
+ prefs_(prefs),
+ request_context_(request_context),
+ gcm_enabled_(true),
+ poll_interval_seconds_(
+ GCMChannelStatusRequest::default_poll_interval_seconds()),
+ delay_removed_for_testing_(false),
+ weak_ptr_factory_(this) {
+ gcm_enabled_ = prefs_->GetBoolean(kGCMChannelStatus);
+ poll_interval_seconds_ = prefs_->GetInteger(kGCMChannelPollIntervalSeconds);
+ if (poll_interval_seconds_ <
+ GCMChannelStatusRequest::min_poll_interval_seconds()) {
+ poll_interval_seconds_ =
+ GCMChannelStatusRequest::min_poll_interval_seconds();
+ }
+ last_check_time_ = base::Time::FromInternalValue(
+ prefs_->GetInt64(kGCMChannelLastCheckTime));
+}
+
+GCMChannelStatusSyncer::~GCMChannelStatusSyncer() {
+}
+
+void GCMChannelStatusSyncer::EnsureStarted() {
+ // Bail out if the request is already scheduled or started.
+ if (weak_ptr_factory_.HasWeakPtrs() || request_)
+ return;
+
+ ScheduleRequest();
+}
+
+void GCMChannelStatusSyncer::Stop() {
+ request_.reset();
+ weak_ptr_factory_.InvalidateWeakPtrs();
+}
+
+void GCMChannelStatusSyncer::OnRequestCompleted(bool enabled,
+ int poll_interval_seconds) {
+ DCHECK(request_);
+ request_.reset();
+
+ // Persist the current time as the last request complete time.
+ last_check_time_ = base::Time::Now();
+ prefs_->SetInt64(kGCMChannelLastCheckTime,
+ last_check_time_.ToInternalValue());
+
+ if (gcm_enabled_ != enabled) {
+ gcm_enabled_ = enabled;
+ prefs_->SetBoolean(kGCMChannelStatus, enabled);
+ if (gcm_enabled_)
+ driver_->Enable();
+ else
+ driver_->Disable();
+ }
+
+ DCHECK_GE(poll_interval_seconds,
+ GCMChannelStatusRequest::min_poll_interval_seconds());
+ if (poll_interval_seconds_ != poll_interval_seconds) {
+ poll_interval_seconds_ = poll_interval_seconds;
+ prefs_->SetInteger(kGCMChannelPollIntervalSeconds, poll_interval_seconds_);
+ }
+
+ ScheduleRequest();
+}
+
+void GCMChannelStatusSyncer::ScheduleRequest() {
+ current_request_delay_interval_ = GetRequestDelayInterval();
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&GCMChannelStatusSyncer::StartRequest,
+ weak_ptr_factory_.GetWeakPtr()),
+ current_request_delay_interval_);
+}
+
+void GCMChannelStatusSyncer::StartRequest() {
+ DCHECK(!request_);
+
+ request_.reset(new GCMChannelStatusRequest(
+ request_context_,
+ base::Bind(&GCMChannelStatusSyncer::OnRequestCompleted,
+ weak_ptr_factory_.GetWeakPtr())));
+ request_->Start();
+}
+
+base::TimeDelta GCMChannelStatusSyncer::GetRequestDelayInterval() const {
+ // No delay during testing.
+ if (delay_removed_for_testing_)
+ return base::TimeDelta();
+
+ // Make sure that checking with server occurs at polling interval, regardless
+ // whether the browser restarts.
+ int64 delay_seconds = poll_interval_seconds_ -
+ (base::Time::Now() - last_check_time_).InSeconds();
+ if (delay_seconds < 0)
+ delay_seconds = 0;
+
+ if (last_check_time_.is_null()) {
+ // For the first-time request, add a small delay to avoid sending request at
+ // browser startup time.
+ DCHECK(!delay_seconds);
+ delay_seconds = kFirstTimeDelaySeconds;
+ } else {
+ // Otherwise, add a fuzzing variation to the delay.
+ delay_seconds += base::RandInt(0, kGCMChannelRequestTimeJitterSeconds);
+ }
+
+ return base::TimeDelta::FromSeconds(delay_seconds);
+}
+
+} // namespace gcm
diff --git a/components/gcm_driver/gcm_channel_status_syncer.h b/components/gcm_driver/gcm_channel_status_syncer.h
new file mode 100644
index 0000000..83cfbda3
--- /dev/null
+++ b/components/gcm_driver/gcm_channel_status_syncer.h
@@ -0,0 +1,98 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_GCM_DRIVER_GCM_CHANNEL_STATUS_SYNCER_H_
+#define COMPONENTS_GCM_DRIVER_GCM_CHANNEL_STATUS_SYNCER_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/time/time.h"
+
+class PrefService;
+class PrefRegistrySimple;
+
+namespace net {
+class URLRequestContextGetter;
+}
+
+namespace user_prefs {
+class PrefRegistrySyncable;
+}
+
+namespace gcm {
+
+class GCMChannelStatusRequest;
+class GCMDriver;
+
+// Syncing with the server for GCM channel status that controls if GCM
+// functionality should be enabled or disabled.
+class GCMChannelStatusSyncer {
+ public:
+ static void RegisterPrefs(PrefRegistrySimple* registry);
+ static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
+
+ GCMChannelStatusSyncer(
+ GCMDriver* driver,
+ PrefService* prefs,
+ const scoped_refptr<net::URLRequestContextGetter>& request_context);
+ ~GCMChannelStatusSyncer();
+
+ void EnsureStarted();
+ void Stop();
+
+ bool gcm_enabled() const { return gcm_enabled_; }
+
+ // For testing purpose.
+ void set_delay_removed_for_testing(bool delay_removed) {
+ delay_removed_for_testing_ = delay_removed;
+ }
+ base::TimeDelta current_request_delay_interval() const {
+ return current_request_delay_interval_;
+ }
+ static int first_time_delay_seconds();
+
+ private:
+ // Called when a request is completed.
+ void OnRequestCompleted(bool enabled, int poll_interval_seconds);
+
+ // Schedules next request to start after appropriate delay.
+ void ScheduleRequest();
+
+ // Creates and starts a request immediately.
+ void StartRequest();
+
+ // Computes and returns a delay with the fuzzing variation added if needed,
+ // after which the request could start.
+ base::TimeDelta GetRequestDelayInterval() const;
+
+ // GCMDriver owns GCMChannelStatusSyncer instance.
+ GCMDriver* driver_;
+ PrefService* prefs_;
+
+ scoped_refptr<net::URLRequestContextGetter> request_context_;
+ scoped_ptr<GCMChannelStatusRequest> request_;
+
+ bool gcm_enabled_;
+ int poll_interval_seconds_;
+ base::Time last_check_time_;
+
+ // The flag that indicates if the delay, including fuzzing variation and poll
+ // interval, is removed for testing purpose.
+ bool delay_removed_for_testing_;
+
+ // Tracked for testing purpose.
+ base::TimeDelta current_request_delay_interval_;
+
+ // Used to pass a weak pointer to a task.
+ base::WeakPtrFactory<GCMChannelStatusSyncer> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(GCMChannelStatusSyncer);
+};
+
+} // namespace gcm
+
+#endif // COMPONENTS_GCM_DRIVER_GCM_CHANNEL_STATUS_SYNCER_H_
diff --git a/components/gcm_driver/gcm_driver_desktop.cc b/components/gcm_driver/gcm_driver_desktop.cc
index 8d35c2c..f646f07 100644
--- a/components/gcm_driver/gcm_driver_desktop.cc
+++ b/components/gcm_driver/gcm_driver_desktop.cc
@@ -14,6 +14,7 @@
#include "base/sequenced_task_runner.h"
#include "base/threading/sequenced_worker_pool.h"
#include "components/gcm_driver/gcm_app_handler.h"
+#include "components/gcm_driver/gcm_channel_status_syncer.h"
#include "components/gcm_driver/gcm_client_factory.h"
#include "components/gcm_driver/gcm_delayed_task_controller.h"
#include "components/gcm_driver/system_encryptor.h"
@@ -329,18 +330,22 @@ void GCMDriverDesktop::IOWorker::RemoveAccountMapping(
GCMDriverDesktop::GCMDriverDesktop(
scoped_ptr<GCMClientFactory> gcm_client_factory,
const GCMClient::ChromeBuildInfo& chrome_build_info,
+ PrefService* prefs,
const base::FilePath& store_path,
const scoped_refptr<net::URLRequestContextGetter>& request_context,
const scoped_refptr<base::SequencedTaskRunner>& ui_thread,
const scoped_refptr<base::SequencedTaskRunner>& io_thread,
const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner)
- : signed_in_(false),
+ : gcm_channel_status_syncer_(this, prefs, request_context),
+ signed_in_(false),
gcm_started_(false),
gcm_enabled_(true),
connected_(false),
ui_thread_(ui_thread),
io_thread_(io_thread),
weak_ptr_factory_(this) {
+ gcm_enabled_ = gcm_channel_status_syncer_.gcm_enabled();
+
// Create and initialize the GCMClient. Note that this does not initiate the
// GCM check-in.
io_worker_.reset(new IOWorker(ui_thread, io_thread));
@@ -442,6 +447,8 @@ void GCMDriverDesktop::Stop() {
if (!gcm_started_)
return;
+ gcm_channel_status_syncer_.Stop();
+
RemoveCachedData();
io_thread_->PostTask(
@@ -627,6 +634,11 @@ GCMClient::Result GCMDriverDesktop::EnsureStarted() {
DCHECK(!delayed_task_controller_);
delayed_task_controller_.reset(new GCMDelayedTaskController);
+ // Polling for channel status is only needed when GCM is supported for all
+ // users.
+ if (GCMDriver::IsAllowedForAllUsers())
+ gcm_channel_status_syncer_.EnsureStarted();
+
// Note that we need to pass weak pointer again since the existing weak
// pointer in IOWorker might have been invalidated when check-out occurs.
io_thread_->PostTask(
diff --git a/components/gcm_driver/gcm_driver_desktop.h b/components/gcm_driver/gcm_driver_desktop.h
index 51a9845..9ed4fdc 100644
--- a/components/gcm_driver/gcm_driver_desktop.h
+++ b/components/gcm_driver/gcm_driver_desktop.h
@@ -15,10 +15,13 @@
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
+#include "components/gcm_driver/gcm_channel_status_syncer.h"
#include "components/gcm_driver/gcm_client.h"
#include "components/gcm_driver/gcm_connection_observer.h"
#include "components/gcm_driver/gcm_driver.h"
+class PrefService;
+
namespace base {
class FilePath;
class SequencedTaskRunner;
@@ -44,6 +47,7 @@ class GCMDriverDesktop : public GCMDriver {
GCMDriverDesktop(
scoped_ptr<GCMClientFactory> gcm_client_factory,
const GCMClient::ChromeBuildInfo& chrome_build_info,
+ PrefService* prefs,
const base::FilePath& store_path,
const scoped_refptr<net::URLRequestContextGetter>& request_context,
const scoped_refptr<base::SequencedTaskRunner>& ui_thread,
@@ -84,6 +88,12 @@ class GCMDriverDesktop : public GCMDriver {
void SetAccountsForCheckin(
const std::map<std::string, std::string>& account_tokens);
+ // Exposed for testing purpose.
+ bool gcm_enabled() const { return gcm_enabled_; }
+ GCMChannelStatusSyncer* gcm_channel_status_syncer_for_testing() {
+ return &gcm_channel_status_syncer_;
+ }
+
protected:
// GCMDriver implementation:
virtual GCMClient::Result EnsureStarted() OVERRIDE;
@@ -125,6 +135,8 @@ class GCMDriverDesktop : public GCMDriver {
void GetGCMStatisticsFinished(const GCMClient::GCMStatistics& stats);
+ GCMChannelStatusSyncer gcm_channel_status_syncer_;
+
// Flag to indicate whether the user is signed in to a GAIA account.
// TODO(jianli): To be removed when sign-in enforcement is dropped.
bool signed_in_;
@@ -133,6 +145,7 @@ class GCMDriverDesktop : public GCMDriver {
bool gcm_started_;
// Flag to indicate if GCM is enabled.
+ // TODO(jianli): Removed when we switch completely to support all users.
bool gcm_enabled_;
// Flag to indicate the last known state of the GCM client. Because this
diff --git a/components/gcm_driver/gcm_driver_desktop_unittest.cc b/components/gcm_driver/gcm_driver_desktop_unittest.cc
index ad95f86..8a25768 100644
--- a/components/gcm_driver/gcm_driver_desktop_unittest.cc
+++ b/components/gcm_driver/gcm_driver_desktop_unittest.cc
@@ -11,6 +11,8 @@
#include "base/message_loop/message_loop.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/metrics/field_trial.h"
+#include "base/prefs/pref_registry_simple.h"
+#include "base/prefs/testing_pref_service.h"
#include "base/run_loop.h"
#include "base/strings/string_util.h"
#include "base/test/test_simple_task_runner.h"
@@ -19,8 +21,13 @@
#include "components/gcm_driver/fake_gcm_client.h"
#include "components/gcm_driver/fake_gcm_client_factory.h"
#include "components/gcm_driver/gcm_app_handler.h"
+#include "components/gcm_driver/gcm_channel_status_request.h"
+#include "components/gcm_driver/gcm_channel_status_syncer.h"
#include "components/gcm_driver/gcm_client_factory.h"
#include "components/gcm_driver/gcm_connection_observer.h"
+#include "components/gcm_driver/proto/gcm_channel_status.pb.h"
+#include "net/url_request/test_url_fetcher_factory.h"
+#include "net/url_request/url_fetcher_delegate.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"
@@ -97,7 +104,7 @@ class GCMDriverTest : public testing::Test {
virtual void SetUp() OVERRIDE;
virtual void TearDown() OVERRIDE;
- GCMDriver* driver() { return driver_.get(); }
+ GCMDriverDesktop* driver() { return driver_.get(); }
FakeGCMAppHandler* gcm_app_handler() { return gcm_app_handler_.get(); }
FakeGCMConnectionObserver* gcm_connection_observer() {
return gcm_connection_observer_.get();
@@ -118,6 +125,7 @@ class GCMDriverTest : public testing::Test {
FakeGCMClient* GetGCMClient();
void CreateDriver(FakeGCMClient::StartMode gcm_client_start_mode);
+ void ShutdownDriver();
void AddAppHandlers();
void RemoveAppHandlers();
@@ -142,11 +150,12 @@ class GCMDriverTest : public testing::Test {
void UnregisterCompleted(GCMClient::Result result);
base::ScopedTempDir temp_dir_;
+ TestingPrefServiceSimple prefs_;
scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
base::MessageLoopForUI message_loop_;
base::Thread io_thread_;
base::FieldTrialList field_trial_list_;
- scoped_ptr<GCMDriver> driver_;
+ scoped_ptr<GCMDriverDesktop> driver_;
scoped_ptr<FakeGCMAppHandler> gcm_app_handler_;
scoped_ptr<FakeGCMConnectionObserver> gcm_connection_observer_;
@@ -174,6 +183,7 @@ GCMDriverTest::~GCMDriverTest() {
}
void GCMDriverTest::SetUp() {
+ GCMChannelStatusSyncer::RegisterPrefs(prefs_.registry());
io_thread_.Start();
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
}
@@ -182,9 +192,7 @@ void GCMDriverTest::TearDown() {
if (!driver_)
return;
- if (gcm_connection_observer_.get())
- driver_->RemoveConnectionObserver(gcm_connection_observer_.get());
- driver_->Shutdown();
+ ShutdownDriver();
driver_.reset();
PumpIOLoop();
@@ -229,6 +237,7 @@ void GCMDriverTest::CreateDriver(
base::MessageLoopProxy::current(),
io_thread_.message_loop_proxy())).Pass(),
GCMClient::ChromeBuildInfo(),
+ &prefs_,
temp_dir_.path(),
request_context,
base::MessageLoopProxy::current(),
@@ -241,6 +250,12 @@ void GCMDriverTest::CreateDriver(
driver_->AddConnectionObserver(gcm_connection_observer_.get());
}
+void GCMDriverTest::ShutdownDriver() {
+ if (gcm_connection_observer())
+ driver()->RemoveConnectionObserver(gcm_connection_observer());
+ driver()->Shutdown();
+}
+
void GCMDriverTest::AddAppHandlers() {
driver_->AddAppHandler(kTestAppID1, gcm_app_handler_.get());
driver_->AddAppHandler(kTestAppID2, gcm_app_handler_.get());
@@ -386,7 +401,7 @@ TEST_F(GCMDriverTest, Shutdown) {
AddAppHandlers();
EXPECT_TRUE(HasAppHandlers());
- driver()->Shutdown();
+ ShutdownDriver();
EXPECT_FALSE(HasAppHandlers());
EXPECT_FALSE(driver()->IsConnected());
EXPECT_FALSE(gcm_connection_observer()->connected());
@@ -941,4 +956,247 @@ TEST_F(GCMDriverFunctionalTest, MessagesDeleted) {
EXPECT_EQ(kTestAppID1, gcm_app_handler()->app_id());
}
+// Tests a single instance of GCMDriver.
+class GCMChannelStatusSyncerTest : public GCMDriverTest {
+ public:
+ GCMChannelStatusSyncerTest();
+ virtual ~GCMChannelStatusSyncerTest();
+
+ // testing::Test:
+ virtual void SetUp() OVERRIDE;
+
+ void CompleteGCMChannelStatusRequest(bool enabled, int poll_interval_seconds);
+ bool CompareDelaySeconds(bool expected_delay_seconds,
+ bool actual_delay_seconds);
+
+ GCMChannelStatusSyncer* syncer() {
+ return driver()->gcm_channel_status_syncer_for_testing();
+ }
+
+ private:
+ net::TestURLFetcherFactory url_fetcher_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(GCMChannelStatusSyncerTest);
+};
+
+GCMChannelStatusSyncerTest::GCMChannelStatusSyncerTest() {
+}
+
+GCMChannelStatusSyncerTest::~GCMChannelStatusSyncerTest() {
+}
+
+void GCMChannelStatusSyncerTest::SetUp() {
+ GCMDriverTest::SetUp();
+
+ // Turn on all-user support.
+ ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial("GCM", "Enabled"));
+}
+
+void GCMChannelStatusSyncerTest::CompleteGCMChannelStatusRequest(
+ bool enabled, int poll_interval_seconds) {
+ gcm_proto::ExperimentStatusResponse response_proto;
+ response_proto.mutable_gcm_channel()->set_enabled(enabled);
+
+ if (poll_interval_seconds)
+ response_proto.set_poll_interval_seconds(poll_interval_seconds);
+
+ std::string response_string;
+ response_proto.SerializeToString(&response_string);
+
+ net::TestURLFetcher* fetcher = url_fetcher_factory_.GetFetcherByID(0);
+ ASSERT_TRUE(fetcher);
+ fetcher->set_response_code(net::HTTP_OK);
+ fetcher->SetResponseString(response_string);
+ fetcher->delegate()->OnURLFetchComplete(fetcher);
+}
+
+bool GCMChannelStatusSyncerTest::CompareDelaySeconds(
+ bool expected_delay_seconds, bool actual_delay_seconds) {
+ // Most of time, the actual delay should not be smaller than the expected
+ // delay.
+ if (actual_delay_seconds >= expected_delay_seconds)
+ return true;
+ // It is also OK that the actual delay is a bit smaller than the expected
+ // delay in case that the test runs slowly.
+ return expected_delay_seconds - actual_delay_seconds < 30;
+}
+
+TEST_F(GCMChannelStatusSyncerTest, DisableAndEnable) {
+ // Create GCMDriver first. GCM is not started.
+ CreateDriver(FakeGCMClient::NO_DELAY_START);
+ EXPECT_FALSE(driver()->IsStarted());
+
+ // By default, GCM is enabled.
+ EXPECT_TRUE(driver()->gcm_enabled());
+ EXPECT_TRUE(syncer()->gcm_enabled());
+
+ // Remove delay such that the request could be executed immediately.
+ syncer()->set_delay_removed_for_testing(true);
+
+ // GCM will be started after app handler is added.
+ AddAppHandlers();
+ EXPECT_TRUE(driver()->IsStarted());
+
+ // GCM is still enabled at this point.
+ EXPECT_TRUE(driver()->gcm_enabled());
+ EXPECT_TRUE(syncer()->gcm_enabled());
+
+ // Wait until the GCM channel status request gets triggered.
+ PumpUILoop();
+
+ // Complete the request that disables the GCM.
+ CompleteGCMChannelStatusRequest(false, 0);
+ EXPECT_FALSE(driver()->gcm_enabled());
+ EXPECT_FALSE(syncer()->gcm_enabled());
+ EXPECT_FALSE(driver()->IsStarted());
+
+ // Wait until next GCM channel status request gets triggered.
+ PumpUILoop();
+
+ // Complete the request that enables the GCM.
+ CompleteGCMChannelStatusRequest(true, 0);
+ EXPECT_TRUE(driver()->gcm_enabled());
+ EXPECT_TRUE(syncer()->gcm_enabled());
+ EXPECT_TRUE(driver()->IsStarted());
+}
+
+TEST_F(GCMChannelStatusSyncerTest, DisableAndRestart) {
+ // Create GCMDriver first. GCM is not started.
+ CreateDriver(FakeGCMClient::NO_DELAY_START);
+ EXPECT_FALSE(driver()->IsStarted());
+
+ // By default, GCM is enabled.
+ EXPECT_TRUE(driver()->gcm_enabled());
+ EXPECT_TRUE(syncer()->gcm_enabled());
+
+ // Remove delay such that the request could be executed immediately.
+ syncer()->set_delay_removed_for_testing(true);
+
+ // GCM will be started after app handler is added.
+ AddAppHandlers();
+ EXPECT_TRUE(driver()->IsStarted());
+
+ // GCM is still enabled at this point.
+ EXPECT_TRUE(driver()->gcm_enabled());
+ EXPECT_TRUE(syncer()->gcm_enabled());
+
+ // Wait until the GCM channel status request gets triggered.
+ PumpUILoop();
+
+ // Complete the request that disables the GCM.
+ CompleteGCMChannelStatusRequest(false, 0);
+ EXPECT_FALSE(driver()->gcm_enabled());
+ EXPECT_FALSE(syncer()->gcm_enabled());
+ EXPECT_FALSE(driver()->IsStarted());
+
+ // Simulate browser start by recreating GCMDriver.
+ ShutdownDriver();
+ CreateDriver(FakeGCMClient::NO_DELAY_START);
+
+ // GCM is still disabled.
+ EXPECT_FALSE(driver()->gcm_enabled());
+ EXPECT_FALSE(syncer()->gcm_enabled());
+ EXPECT_FALSE(driver()->IsStarted());
+
+ AddAppHandlers();
+ EXPECT_FALSE(driver()->gcm_enabled());
+ EXPECT_FALSE(syncer()->gcm_enabled());
+ EXPECT_FALSE(driver()->IsStarted());
+}
+
+TEST_F(GCMChannelStatusSyncerTest, FirstTimePolling) {
+ // Start GCM.
+ CreateDriver(FakeGCMClient::NO_DELAY_START);
+ AddAppHandlers();
+
+ // The 1st request should be triggered shortly without jittering.
+ EXPECT_EQ(GCMChannelStatusSyncer::first_time_delay_seconds(),
+ syncer()->current_request_delay_interval().InSeconds());
+}
+
+TEST_F(GCMChannelStatusSyncerTest, SubsequentPollingWithDefaultInterval) {
+ // Create GCMDriver first. GCM is not started.
+ CreateDriver(FakeGCMClient::NO_DELAY_START);
+
+ // Remove delay such that the request could be executed immediately.
+ syncer()->set_delay_removed_for_testing(true);
+
+ // Now GCM is started.
+ AddAppHandlers();
+
+ // Wait until the GCM channel status request gets triggered.
+ PumpUILoop();
+
+ // Keep delay such that we can find out the computed delay time.
+ syncer()->set_delay_removed_for_testing(false);
+
+ // Complete the request. The default interval is intact.
+ CompleteGCMChannelStatusRequest(true, 0);
+
+ // The next request should be scheduled at the expected default interval.
+ int64 actual_delay_seconds =
+ syncer()->current_request_delay_interval().InSeconds();
+ int64 expected_delay_seconds =
+ GCMChannelStatusRequest::default_poll_interval_seconds();
+ EXPECT_TRUE(CompareDelaySeconds(expected_delay_seconds, actual_delay_seconds))
+ << "expected delay: " << expected_delay_seconds
+ << " actual delay: " << actual_delay_seconds;
+
+ // Simulate browser start by recreating GCMDriver.
+ ShutdownDriver();
+ CreateDriver(FakeGCMClient::NO_DELAY_START);
+ AddAppHandlers();
+
+ // After start-up, the request should still be scheduled at the expected
+ // default interval.
+ actual_delay_seconds =
+ syncer()->current_request_delay_interval().InSeconds();
+ EXPECT_TRUE(CompareDelaySeconds(expected_delay_seconds, actual_delay_seconds))
+ << "expected delay: " << expected_delay_seconds
+ << " actual delay: " << actual_delay_seconds;
+}
+
+TEST_F(GCMChannelStatusSyncerTest, SubsequentPollingWithUpdatedInterval) {
+ // Create GCMDriver first. GCM is not started.
+ CreateDriver(FakeGCMClient::NO_DELAY_START);
+
+ // Remove delay such that the request could be executed immediately.
+ syncer()->set_delay_removed_for_testing(true);
+
+ // Now GCM is started.
+ AddAppHandlers();
+
+ // Wait until the GCM channel status request gets triggered.
+ PumpUILoop();
+
+ // Keep delay such that we can find out the computed delay time.
+ syncer()->set_delay_removed_for_testing(false);
+
+ // Complete the request. The interval is being changed.
+ int new_poll_interval_seconds =
+ GCMChannelStatusRequest::default_poll_interval_seconds() * 2;
+ CompleteGCMChannelStatusRequest(true, new_poll_interval_seconds);
+
+ // The next request should be scheduled at the expected updated interval.
+ int64 actual_delay_seconds =
+ syncer()->current_request_delay_interval().InSeconds();
+ int64 expected_delay_seconds = new_poll_interval_seconds;
+ EXPECT_TRUE(CompareDelaySeconds(expected_delay_seconds, actual_delay_seconds))
+ << "expected delay: " << expected_delay_seconds
+ << " actual delay: " << actual_delay_seconds;
+
+ // Simulate browser start by recreating GCMDriver.
+ ShutdownDriver();
+ CreateDriver(FakeGCMClient::NO_DELAY_START);
+ AddAppHandlers();
+
+ // After start-up, the request should still be scheduled at the expected
+ // updated interval.
+ actual_delay_seconds =
+ syncer()->current_request_delay_interval().InSeconds();
+ EXPECT_TRUE(CompareDelaySeconds(expected_delay_seconds, actual_delay_seconds))
+ << "expected delay: " << expected_delay_seconds
+ << " actual delay: " << actual_delay_seconds;
+}
+
} // namespace gcm