[Sync] Add sync invalidations factory

This CL introduces a factory for SyncInvalidationsService and wires it
to ProfileSyncService to add listener. The invalidations service is
added behind a feature toggle which is disabled by default.

Bug: 1082122
Change-Id: I94249979ae4fc17132670fba2d49a7ba50a56bc1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2220029
Commit-Queue: Rushan Suleymanov <[email protected]>
Reviewed-by: Marc Treib <[email protected]>
Cr-Commit-Position: refs/heads/master@{#793132}
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index a7790d7..0909092 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1712,6 +1712,8 @@
     "sync/sessions/sync_sessions_web_contents_router_factory.h",
     "sync/sync_encryption_keys_tab_helper.cc",
     "sync/sync_encryption_keys_tab_helper.h",
+    "sync/sync_invalidations_service_factory.cc",
+    "sync/sync_invalidations_service_factory.h",
     "sync/sync_startup_tracker.cc",
     "sync/sync_startup_tracker.h",
     "sync/user_event_service_factory.cc",
diff --git a/chrome/browser/sync/chrome_sync_client.cc b/chrome/browser/sync/chrome_sync_client.cc
index 8cf31d0..07fc03dd 100644
--- a/chrome/browser/sync/chrome_sync_client.cc
+++ b/chrome/browser/sync/chrome_sync_client.cc
@@ -36,6 +36,7 @@
 #include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/browser/sync/send_tab_to_self_sync_service_factory.h"
 #include "chrome/browser/sync/session_sync_service_factory.h"
+#include "chrome/browser/sync/sync_invalidations_service_factory.h"
 #include "chrome/browser/sync/user_event_service_factory.h"
 #include "chrome/browser/themes/theme_service.h"
 #include "chrome/browser/themes/theme_service_factory.h"
@@ -544,6 +545,11 @@
   return nullptr;
 }
 
+syncer::SyncInvalidationsService*
+ChromeSyncClient::GetSyncInvalidationsService() {
+  return SyncInvalidationsServiceFactory::GetForProfile(profile_);
+}
+
 scoped_refptr<syncer::ExtensionsActivity>
 ChromeSyncClient::GetExtensionsActivity() {
   return extensions_activity_monitor_.GetExtensionsActivity();
diff --git a/chrome/browser/sync/chrome_sync_client.h b/chrome/browser/sync/chrome_sync_client.h
index 3d34abb..32d9fd3 100644
--- a/chrome/browser/sync/chrome_sync_client.h
+++ b/chrome/browser/sync/chrome_sync_client.h
@@ -56,6 +56,7 @@
       syncer::SyncService* sync_service) override;
   syncer::TrustedVaultClient* GetTrustedVaultClient() override;
   invalidation::InvalidationService* GetInvalidationService() override;
+  syncer::SyncInvalidationsService* GetSyncInvalidationsService() override;
   BookmarkUndoService* GetBookmarkUndoService() override;
   scoped_refptr<syncer::ExtensionsActivity> GetExtensionsActivity() override;
   base::WeakPtr<syncer::SyncableService> GetSyncableServiceForType(
diff --git a/chrome/browser/sync/profile_sync_service_factory.cc b/chrome/browser/sync/profile_sync_service_factory.cc
index 1918a339..51deffd 100644
--- a/chrome/browser/sync/profile_sync_service_factory.cc
+++ b/chrome/browser/sync/profile_sync_service_factory.cc
@@ -38,6 +38,7 @@
 #include "chrome/browser/sync/model_type_store_service_factory.h"
 #include "chrome/browser/sync/send_tab_to_self_sync_service_factory.h"
 #include "chrome/browser/sync/session_sync_service_factory.h"
+#include "chrome/browser/sync/sync_invalidations_service_factory.h"
 #include "chrome/browser/sync/user_event_service_factory.h"
 #include "chrome/browser/themes/theme_service_factory.h"
 #include "chrome/browser/undo/bookmark_undo_service_factory.h"
@@ -150,6 +151,7 @@
   DependsOn(HistoryServiceFactory::GetInstance());
   DependsOn(IdentityManagerFactory::GetInstance());
   DependsOn(invalidation::ProfileInvalidationProviderFactory::GetInstance());
+  DependsOn(SyncInvalidationsServiceFactory::GetInstance());
   DependsOn(ModelTypeStoreServiceFactory::GetInstance());
   DependsOn(PasswordStoreFactory::GetInstance());
   DependsOn(SecurityEventRecorderFactory::GetInstance());
diff --git a/chrome/browser/sync/sync_invalidations_service_factory.cc b/chrome/browser/sync/sync_invalidations_service_factory.cc
new file mode 100644
index 0000000..e460f298
--- /dev/null
+++ b/chrome/browser/sync/sync_invalidations_service_factory.cc
@@ -0,0 +1,57 @@
+// Copyright 2020 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/sync/sync_invalidations_service_factory.h"
+
+#include "chrome/browser/gcm/gcm_profile_service_factory.h"
+#include "chrome/browser/gcm/instance_id/instance_id_profile_service_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/gcm_driver/gcm_profile_service.h"
+#include "components/gcm_driver/instance_id/instance_id_profile_service.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "components/sync/invalidations/switches.h"
+#include "components/sync/invalidations/sync_invalidations_service_impl.h"
+
+// TODO(crbug.com/1082115): change to real sync sender id: 8181035976.
+constexpr char kDefaultSenderId[] = "361488507004";
+constexpr char kDefaultApplicationId[] = "com.google.chrome.sync.invalidations";
+
+syncer::SyncInvalidationsService*
+SyncInvalidationsServiceFactory::GetForProfile(Profile* profile) {
+  return static_cast<syncer::SyncInvalidationsService*>(
+      GetInstance()->GetServiceForBrowserContext(profile, /*create=*/true));
+}
+
+SyncInvalidationsServiceFactory*
+SyncInvalidationsServiceFactory::GetInstance() {
+  static base::NoDestructor<SyncInvalidationsServiceFactory> instance;
+  return instance.get();
+}
+
+SyncInvalidationsServiceFactory::SyncInvalidationsServiceFactory()
+    : BrowserContextKeyedServiceFactory(
+          "SyncInvalidationsService",
+          BrowserContextDependencyManager::GetInstance()) {
+  DependsOn(gcm::GCMProfileServiceFactory::GetInstance());
+  DependsOn(instance_id::InstanceIDProfileServiceFactory::GetInstance());
+}
+
+SyncInvalidationsServiceFactory::~SyncInvalidationsServiceFactory() = default;
+
+KeyedService* SyncInvalidationsServiceFactory::BuildServiceInstanceFor(
+    content::BrowserContext* context) const {
+  if (!base::FeatureList::IsEnabled(switches::kSubscribeForSyncInvalidations)) {
+    return nullptr;
+  }
+
+  Profile* profile = Profile::FromBrowserContext(context);
+
+  gcm::GCMDriver* gcm_driver =
+      gcm::GCMProfileServiceFactory::GetForProfile(profile)->driver();
+  instance_id::InstanceIDDriver* instance_id_driver =
+      instance_id::InstanceIDProfileServiceFactory::GetForProfile(profile)
+          ->driver();
+  return new syncer::SyncInvalidationsServiceImpl(
+      gcm_driver, instance_id_driver, kDefaultSenderId, kDefaultApplicationId);
+}
diff --git a/chrome/browser/sync/sync_invalidations_service_factory.h b/chrome/browser/sync/sync_invalidations_service_factory.h
new file mode 100644
index 0000000..2edd651
--- /dev/null
+++ b/chrome/browser/sync/sync_invalidations_service_factory.h
@@ -0,0 +1,43 @@
+// Copyright 2020 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_SYNC_SYNC_INVALIDATIONS_SERVICE_FACTORY_H_
+#define CHROME_BROWSER_SYNC_SYNC_INVALIDATIONS_SERVICE_FACTORY_H_
+
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+
+#include "base/no_destructor.h"
+
+class Profile;
+
+namespace syncer {
+class SyncInvalidationsService;
+}  // namespace syncer
+
+class SyncInvalidationsServiceFactory
+    : public BrowserContextKeyedServiceFactory {
+ public:
+  SyncInvalidationsServiceFactory(const SyncInvalidationsServiceFactory&) =
+      delete;
+  SyncInvalidationsServiceFactory& operator=(
+      const SyncInvalidationsServiceFactory&) = delete;
+
+  // Returned value may be nullptr in case if sync invalidations are disabled or
+  // not supported.
+  static syncer::SyncInvalidationsService* GetForProfile(Profile* profile);
+
+  static SyncInvalidationsServiceFactory* GetInstance();
+
+ private:
+  friend class base::NoDestructor<SyncInvalidationsServiceFactory>;
+
+  SyncInvalidationsServiceFactory();
+  ~SyncInvalidationsServiceFactory() override;
+
+  // BrowserContextKeyedServiceFactory:
+  KeyedService* BuildServiceInstanceFor(
+      content::BrowserContext* context) const override;
+};
+
+#endif  // CHROME_BROWSER_SYNC_SYNC_INVALIDATIONS_SERVICE_FACTORY_H_
diff --git a/components/browser_sync/BUILD.gn b/components/browser_sync/BUILD.gn
index efbc49f..b13d47c 100644
--- a/components/browser_sync/BUILD.gn
+++ b/components/browser_sync/BUILD.gn
@@ -27,6 +27,7 @@
     "//components/prefs",
     "//components/reading_list/features:flags",
     "//components/send_tab_to_self",
+    "//components/sync/invalidations",
     "//components/sync_bookmarks",
     "//components/sync_sessions",
     "//components/sync_user_events",
diff --git a/components/browser_sync/profile_sync_components_factory_impl.cc b/components/browser_sync/profile_sync_components_factory_impl.cc
index a9f4045..1d6a709 100644
--- a/components/browser_sync/profile_sync_components_factory_impl.cc
+++ b/components/browser_sync/profile_sync_components_factory_impl.cc
@@ -34,6 +34,7 @@
 #include "components/sync/driver/sync_driver_switches.h"
 #include "components/sync/driver/syncable_service_based_model_type_controller.h"
 #include "components/sync/engine/sync_engine.h"
+#include "components/sync/invalidations/sync_invalidations_service.h"
 #include "components/sync/model/model_type_store_service.h"
 #include "components/sync/model_impl/forwarding_model_type_controller_delegate.h"
 #include "components/sync/model_impl/proxy_model_type_controller_delegate.h"
@@ -360,9 +361,10 @@
 ProfileSyncComponentsFactoryImpl::CreateSyncEngine(
     const std::string& name,
     invalidation::InvalidationService* invalidator,
+    syncer::SyncInvalidationsService* sync_invalidation_service,
     const base::WeakPtr<syncer::SyncPrefs>& sync_prefs) {
   return std::make_unique<syncer::SyncEngineImpl>(
-      name, invalidator, sync_prefs,
+      name, invalidator, sync_invalidation_service, sync_prefs,
       sync_client_->GetModelTypeStoreService()->GetSyncDataPath());
 }
 
diff --git a/components/browser_sync/profile_sync_components_factory_impl.h b/components/browser_sync/profile_sync_components_factory_impl.h
index defee735..054f328 100644
--- a/components/browser_sync/profile_sync_components_factory_impl.h
+++ b/components/browser_sync/profile_sync_components_factory_impl.h
@@ -19,6 +19,7 @@
 namespace syncer {
 class ModelTypeController;
 class ModelTypeControllerDelegate;
+class SyncInvalidationsService;
 class SyncService;
 }
 
@@ -77,6 +78,7 @@
   std::unique_ptr<syncer::SyncEngine> CreateSyncEngine(
       const std::string& name,
       invalidation::InvalidationService* invalidator,
+      syncer::SyncInvalidationsService* sync_invalidation_service,
       const base::WeakPtr<syncer::SyncPrefs>& sync_prefs) override;
 
  private:
diff --git a/components/sync/BUILD.gn b/components/sync/BUILD.gn
index 90f3724..6c27439 100644
--- a/components/sync/BUILD.gn
+++ b/components/sync/BUILD.gn
@@ -549,7 +549,7 @@
     "//components/signin/public/identity_manager:test_support",
     "//components/sync/base:test_support",
     "//components/sync/driver:test_support",
-    "//components/sync/invalidations",
+    "//components/sync/invalidations:test_support",
     "//components/sync/js:test_support",
     "//components/sync_preferences",
     "//components/sync_preferences:test_support",
diff --git a/components/sync/driver/BUILD.gn b/components/sync/driver/BUILD.gn
index d7a109c..bb590d8 100644
--- a/components/sync/driver/BUILD.gn
+++ b/components/sync/driver/BUILD.gn
@@ -79,7 +79,6 @@
     "//components/invalidation/public",
     "//components/sync:rest_of_sync",
     "//components/sync/base",
-    "//components/sync/invalidations",
     "//components/sync/js",
     "//components/sync/protocol",
     "//components/sync/protocol:util",
@@ -96,6 +95,7 @@
     "//components/os_crypt",
     "//components/prefs",
     "//components/signin/public/identity_manager",
+    "//components/sync/invalidations",
     "//components/version_info",
     "//components/version_info:generate_version_info",
     "//services/network/public/cpp",
@@ -169,6 +169,7 @@
     "//components/sync:test_support_engine",
     "//components/sync:test_support_model",
     "//components/sync/driver",
+    "//components/sync/invalidations:test_support",
     "//components/sync_preferences:test_support",
     "//components/version_info",
     "//components/version_info:generate_version_info",
diff --git a/components/sync/driver/glue/sync_engine_backend.cc b/components/sync/driver/glue/sync_engine_backend.cc
index 28e5948..8741ca3 100644
--- a/components/sync/driver/glue/sync_engine_backend.cc
+++ b/components/sync/driver/glue/sync_engine_backend.cc
@@ -31,6 +31,7 @@
 #include "components/sync/engine/sync_backend_registrar.h"
 #include "components/sync/engine/sync_manager.h"
 #include "components/sync/engine/sync_manager_factory.h"
+#include "components/sync/invalidations/switches.h"
 #include "components/sync/model_impl/forwarding_model_type_controller_delegate.h"
 #include "components/sync/nigori/nigori_model_type_processor.h"
 #include "components/sync/nigori/nigori_storage_impl.h"
@@ -560,8 +561,9 @@
 }
 
 void SyncEngineBackend::DoOnInvalidationReceived(const std::string& payload) {
-  // TODO(crbug.com/1082122): add a DCHECK for a feature toggle.
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(
+      base::FeatureList::IsEnabled(switches::kSubscribeForSyncInvalidations));
   // TODO(crbug.com/1102322): use data types from active or from payload.
   const ModelTypeSet active_datatypes{ModelType::BOOKMARKS};
   for (const ModelType type : active_datatypes) {
diff --git a/components/sync/driver/glue/sync_engine_impl.cc b/components/sync/driver/glue/sync_engine_impl.cc
index cbd0d67..126ffacc 100644
--- a/components/sync/driver/glue/sync_engine_impl.cc
+++ b/components/sync/driver/glue/sync_engine_impl.cc
@@ -30,14 +30,21 @@
 #include "components/sync/engine/sync_engine_host.h"
 #include "components/sync/engine/sync_manager_factory.h"
 #include "components/sync/engine/sync_string_conversions.h"
+#include "components/sync/invalidations/fcm_handler.h"
+#include "components/sync/invalidations/sync_invalidations_service.h"
 
 namespace syncer {
 
-SyncEngineImpl::SyncEngineImpl(const std::string& name,
-                               invalidation::InvalidationService* invalidator,
-                               const base::WeakPtr<SyncPrefs>& sync_prefs,
-                               const base::FilePath& sync_data_folder)
-    : name_(name), sync_prefs_(sync_prefs), invalidator_(invalidator) {
+SyncEngineImpl::SyncEngineImpl(
+    const std::string& name,
+    invalidation::InvalidationService* invalidator,
+    SyncInvalidationsService* sync_invalidations_service,
+    const base::WeakPtr<SyncPrefs>& sync_prefs,
+    const base::FilePath& sync_data_folder)
+    : name_(name),
+      sync_prefs_(sync_prefs),
+      invalidator_(invalidator),
+      sync_invalidations_service_(sync_invalidations_service) {
   backend_ = base::MakeRefCounted<SyncEngineBackend>(
       name_, sync_data_folder, weak_ptr_factory_.GetWeakPtr());
 }
@@ -155,6 +162,12 @@
     invalidator_->UnregisterInvalidationHandler(this);
     invalidator_ = nullptr;
   }
+  if (sync_invalidations_service_) {
+    // It's safe to call RemoveListener even if AddListener wasn't called
+    // before.
+    sync_invalidations_service_->RemoveListener(this);
+    sync_invalidations_service_ = nullptr;
+  }
   last_enabled_types_.Clear();
   invalidation_handler_registered_ = false;
 
@@ -314,6 +327,10 @@
     OnInvalidatorStateChange(invalidator_->GetInvalidatorState());
   }
 
+  if (sync_invalidations_service_) {
+    sync_invalidations_service_->AddListener(this);
+  }
+
   host_->OnEngineInitialized(initial_types, js_backend, debug_info_listener,
                              birthday, bag_of_chips, /*success=*/true);
 }
@@ -441,6 +458,7 @@
   bool success = invalidator_->UpdateInterestedTopics(
       this, ModelTypeSetToTopicSet(enabled_for_invalidation));
   DCHECK(success);
+  // TODO(crbug.com/1102312): update enabled data types for sync invalidations.
 }
 
 void SyncEngineImpl::GetNigoriNodeForDebugging(AllNodesCallback callback) {
@@ -460,6 +478,7 @@
 
 void SyncEngineImpl::OnInvalidationReceived(const std::string& payload) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  // TODO(crbug.com/1082122): check that sync engine is fully initialized.
   sync_task_runner_->PostTask(
       FROM_HERE, base::BindOnce(&SyncEngineBackend::DoOnInvalidationReceived,
                                 backend_, payload));
diff --git a/components/sync/driver/glue/sync_engine_impl.h b/components/sync/driver/glue/sync_engine_impl.h
index 0626b24..c753a98 100644
--- a/components/sync/driver/glue/sync_engine_impl.h
+++ b/components/sync/driver/glue/sync_engine_impl.h
@@ -38,20 +38,22 @@
 
 namespace syncer {
 
-class SyncEngineBackend;
 class SyncBackendRegistrar;
+class SyncEngineBackend;
+class SyncInvalidationsService;
 class SyncPrefs;
 
 // The only real implementation of the SyncEngine. See that interface's
 // definition for documentation of public methods.
 class SyncEngineImpl : public SyncEngine,
                        public InvalidationHandler,
-                       public syncer::InvalidationsListener {
+                       public InvalidationsListener {
  public:
   using Status = SyncStatus;
 
   SyncEngineImpl(const std::string& name,
                  invalidation::InvalidationService* invalidator,
+                 SyncInvalidationsService* sync_invalidations_service,
                  const base::WeakPtr<SyncPrefs>& sync_prefs,
                  const base::FilePath& sync_data_folder);
   ~SyncEngineImpl() override;
@@ -210,8 +212,15 @@
   // A pointer to the registrar; owned by |backend_|.
   SyncBackendRegistrar* registrar_ = nullptr;
 
-  invalidation::InvalidationService* invalidator_;
+  invalidation::InvalidationService* invalidator_ = nullptr;
   bool invalidation_handler_registered_ = false;
+
+  // Sync invalidation service, it may be nullptr if sync invalidations are
+  // disabled or not supported. It doesn't need to have the same as
+  // |invalidation_handler_registered_| flag as the service doesn't have topics
+  // to unsibscribe.
+  SyncInvalidationsService* sync_invalidations_service_ = nullptr;
+
   ModelTypeSet last_enabled_types_;
   bool sessions_invalidation_enabled_ = false;
 
diff --git a/components/sync/driver/glue/sync_engine_impl_unittest.cc b/components/sync/driver/glue/sync_engine_impl_unittest.cc
index c21f11d1..26274a4 100644
--- a/components/sync/driver/glue/sync_engine_impl_unittest.cc
+++ b/components/sync/driver/glue/sync_engine_impl_unittest.cc
@@ -5,6 +5,9 @@
 #include "components/sync/driver/glue/sync_engine_impl.h"
 
 #include <cstddef>
+#include <map>
+#include <set>
+#include <string>
 #include <utility>
 
 #include "base/bind.h"
@@ -39,6 +42,9 @@
 #include "components/sync/engine/passive_model_worker.h"
 #include "components/sync/engine/sync_engine_host_stub.h"
 #include "components/sync/engine/sync_manager_factory.h"
+#include "components/sync/invalidations/mock_sync_invalidations_service.h"
+#include "components/sync/invalidations/switches.h"
+#include "components/sync/invalidations/sync_invalidations_service.h"
 #include "components/sync/test/callback_counter.h"
 #include "components/sync_preferences/pref_service_syncable.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
@@ -48,6 +54,9 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
+using testing::NiceMock;
+using testing::NotNull;
+
 namespace syncer {
 
 namespace {
@@ -188,7 +197,8 @@
     ON_CALL(invalidator_, UpdateInterestedTopics(testing::_, testing::_))
         .WillByDefault(testing::Return(true));
     backend_ = std::make_unique<SyncEngineImpl>(
-        "dummyDebugName", &invalidator_, sync_prefs_->AsWeakPtr(),
+        "dummyDebugName", &invalidator_, GetSyncInvalidationsService(),
+        sync_prefs_->AsWeakPtr(),
         temp_dir_.GetPath().Append(base::FilePath(kTestSyncDir)));
 
     fake_manager_factory_ = std::make_unique<FakeSyncManagerFactory>(
@@ -270,6 +280,12 @@
   }
 
  protected:
+  // Used to initialize SyncEngineImpl. Returns nullptr if there is no sync
+  // invalidations service enabled.
+  virtual SyncInvalidationsService* GetSyncInvalidationsService() {
+    return nullptr;
+  }
+
   void DownloadReady(ModelTypeSet succeeded_types, ModelTypeSet failed_types) {
     engine_types_.PutAll(succeeded_types);
     std::move(quit_loop_).Run();
@@ -304,6 +320,22 @@
   testing::NiceMock<MockInvalidationService> invalidator_;
 };
 
+class SyncEngineImplWithSyncInvalidationsTest : public SyncEngineImplTest {
+ public:
+  SyncEngineImplWithSyncInvalidationsTest() {
+    override_features_.InitAndEnableFeature(
+        switches::kSubscribeForSyncInvalidations);
+  }
+
+ protected:
+  SyncInvalidationsService* GetSyncInvalidationsService() override {
+    return &mock_instance_id_driver_;
+  }
+
+  base::test::ScopedFeatureList override_features_;
+  NiceMock<MockSyncInvalidationsService> mock_instance_id_driver_;
+};
+
 // Test basic initialization with no initial types (first time initialization).
 // Only the nigori should be configured.
 TEST_F(SyncEngineImplTest, InitShutdown) {
@@ -622,7 +654,9 @@
   base::RunLoop().RunUntilIdle();
 }
 
-TEST_F(SyncEngineImplTest, ShouldInvalidateDataTypesOnIncomingInvalidation) {
+TEST_F(SyncEngineImplWithSyncInvalidationsTest,
+       ShouldInvalidateDataTypesOnIncomingInvalidation) {
+  EXPECT_CALL(mock_instance_id_driver_, AddListener(backend_.get()));
   InitializeBackend(/*expect_success=*/true);
   // TODO(crbug.com/1102322): use real payload with invalidated data types.
   backend_->OnInvalidationReceived(/*payload=*/"");
diff --git a/components/sync/driver/profile_sync_service.cc b/components/sync/driver/profile_sync_service.cc
index 3ecea5a..e80c167 100644
--- a/components/sync/driver/profile_sync_service.cc
+++ b/components/sync/driver/profile_sync_service.cc
@@ -534,7 +534,7 @@
 
   engine_ = sync_client_->GetSyncApiComponentFactory()->CreateSyncEngine(
       debug_identifier_, sync_client_->GetInvalidationService(),
-      sync_prefs_.AsWeakPtr());
+      sync_client_->GetSyncInvalidationsService(), sync_prefs_.AsWeakPtr());
 
   // Clear any old errors the first time sync starts.
   if (!user_settings_->IsFirstSetupComplete()) {
diff --git a/components/sync/driver/profile_sync_service_startup_unittest.cc b/components/sync/driver/profile_sync_service_startup_unittest.cc
index 33e99c2..b0db1f5b 100644
--- a/components/sync/driver/profile_sync_service_startup_unittest.cc
+++ b/components/sync/driver/profile_sync_service_startup_unittest.cc
@@ -111,7 +111,7 @@
   FakeSyncEngine* SetUpFakeSyncEngine() {
     auto sync_engine = std::make_unique<FakeSyncEngine>();
     FakeSyncEngine* sync_engine_raw = sync_engine.get();
-    ON_CALL(*component_factory(), CreateSyncEngine(_, _, _))
+    ON_CALL(*component_factory(), CreateSyncEngine(_, _, _, _))
         .WillByDefault(Return(ByMove(std::move(sync_engine))));
     return sync_engine_raw;
   }
@@ -119,7 +119,7 @@
   MockSyncEngine* SetUpMockSyncEngine() {
     auto sync_engine = std::make_unique<NiceMock<MockSyncEngine>>();
     MockSyncEngine* sync_engine_raw = sync_engine.get();
-    ON_CALL(*component_factory(), CreateSyncEngine(_, _, _))
+    ON_CALL(*component_factory(), CreateSyncEngine(_, _, _, _))
         .WillByDefault(Return(ByMove(std::move(sync_engine))));
     return sync_engine_raw;
   }
@@ -477,7 +477,7 @@
   CreateSyncService(ProfileSyncService::MANUAL_START);
 
   // Service should not be started by Initialize() since it's managed.
-  EXPECT_CALL(*component_factory(), CreateSyncEngine(_, _, _)).Times(0);
+  EXPECT_CALL(*component_factory(), CreateSyncEngine(_, _, _, _)).Times(0);
   EXPECT_CALL(*component_factory(), CreateDataTypeManager(_, _, _, _, _, _))
       .Times(0);
   sync_service()->Initialize();
diff --git a/components/sync/driver/profile_sync_service_unittest.cc b/components/sync/driver/profile_sync_service_unittest.cc
index 5f6e68c..fc364f45 100644
--- a/components/sync/driver/profile_sync_service_unittest.cc
+++ b/components/sync/driver/profile_sync_service_unittest.cc
@@ -195,7 +195,7 @@
         profile_sync_service_bundle_.CreateBasicInitParams(
             behavior, std::move(sync_client)));
 
-    ON_CALL(*component_factory(), CreateSyncEngine(_, _, _))
+    ON_CALL(*component_factory(), CreateSyncEngine(_, _, _, _))
         .WillByDefault(ReturnNewFakeSyncEngine());
     ON_CALL(*component_factory(), CreateDataTypeManager(_, _, _, _, _, _))
         .WillByDefault(
@@ -225,7 +225,7 @@
 
     service_ = std::make_unique<ProfileSyncService>(std::move(init_params));
 
-    ON_CALL(*component_factory(), CreateSyncEngine(_, _, _))
+    ON_CALL(*component_factory(), CreateSyncEngine(_, _, _, _))
         .WillByDefault(ReturnNewFakeSyncEngine());
     ON_CALL(*component_factory(), CreateDataTypeManager(_, _, _, _, _, _))
         .WillByDefault(
@@ -372,7 +372,7 @@
 TEST_F(ProfileSyncServiceTest, SuccessfulInitialization) {
   SignIn();
   CreateService(ProfileSyncService::AUTO_START);
-  EXPECT_CALL(*component_factory(), CreateSyncEngine(_, _, _))
+  EXPECT_CALL(*component_factory(), CreateSyncEngine(_, _, _, _))
       .WillOnce(ReturnNewFakeSyncEngine());
   EXPECT_CALL(*component_factory(), CreateDataTypeManager(_, _, _, _, _, _))
       .WillOnce(
@@ -385,7 +385,7 @@
 
 TEST_F(ProfileSyncServiceTest, SuccessfulLocalBackendInitialization) {
   CreateServiceWithLocalSyncBackend();
-  EXPECT_CALL(*component_factory(), CreateSyncEngine(_, _, _))
+  EXPECT_CALL(*component_factory(), CreateSyncEngine(_, _, _, _))
       .WillOnce(ReturnNewFakeSyncEngine());
   EXPECT_CALL(*component_factory(), CreateDataTypeManager(_, _, _, _, _, _))
       .WillOnce(
@@ -540,7 +540,7 @@
 // before the backend initialize call returns.
 TEST_F(ProfileSyncServiceTest, AbortedByShutdown) {
   CreateService(ProfileSyncService::AUTO_START);
-  ON_CALL(*component_factory(), CreateSyncEngine(_, _, _))
+  ON_CALL(*component_factory(), CreateSyncEngine(_, _, _, _))
       .WillByDefault(ReturnNewFakeSyncEngineNoReturn());
 
   SignIn();
@@ -555,7 +555,7 @@
 TEST_F(ProfileSyncServiceTest, EarlyRequestStop) {
   CreateService(ProfileSyncService::AUTO_START);
   // Set up a fake sync engine that will not immediately finish initialization.
-  EXPECT_CALL(*component_factory(), CreateSyncEngine(_, _, _))
+  EXPECT_CALL(*component_factory(), CreateSyncEngine(_, _, _, _))
       .WillOnce(ReturnNewFakeSyncEngineNoReturn());
   SignIn();
   InitializeForNthSync();
@@ -565,7 +565,7 @@
 
   // Request stop. This should immediately restart the service in standalone
   // transport mode.
-  EXPECT_CALL(*component_factory(), CreateSyncEngine(_, _, _))
+  EXPECT_CALL(*component_factory(), CreateSyncEngine(_, _, _, _))
       .WillOnce(ReturnNewFakeSyncEngine());
   service()->GetUserSettings()->SetSyncRequested(false);
   EXPECT_EQ(
@@ -711,7 +711,7 @@
 
   CreateService(ProfileSyncService::AUTO_START);
   SignIn();
-  EXPECT_CALL(*component_factory(), CreateSyncEngine(_, _, _))
+  EXPECT_CALL(*component_factory(), CreateSyncEngine(_, _, _, _))
       .WillOnce(
           Return(ByMove(std::make_unique<FakeSyncEngineCollectCredentials>(
               &init_account_id, base::RepeatingClosure()))));
@@ -754,7 +754,7 @@
 
   CreateService(ProfileSyncService::AUTO_START);
   SignIn();
-  EXPECT_CALL(*component_factory(), CreateSyncEngine(_, _, _))
+  EXPECT_CALL(*component_factory(), CreateSyncEngine(_, _, _, _))
       .WillOnce(
           Return(ByMove(std::make_unique<FakeSyncEngineCollectCredentials>(
               &init_account_id, base::RepeatingClosure()))));
@@ -818,7 +818,7 @@
 
   CreateService(ProfileSyncService::AUTO_START);
   SignIn();
-  EXPECT_CALL(*component_factory(), CreateSyncEngine(_, _, _))
+  EXPECT_CALL(*component_factory(), CreateSyncEngine(_, _, _, _))
       .WillOnce(
           Return(ByMove(std::make_unique<FakeSyncEngineCollectCredentials>(
               &init_account_id, invalidate_credentials_callback))));
@@ -876,7 +876,7 @@
 
   CreateService(ProfileSyncService::AUTO_START);
   SignIn();
-  EXPECT_CALL(*component_factory(), CreateSyncEngine(_, _, _))
+  EXPECT_CALL(*component_factory(), CreateSyncEngine(_, _, _, _))
       .WillOnce(
           Return(ByMove(std::make_unique<FakeSyncEngineCollectCredentials>(
               &init_account_id, base::RepeatingClosure()))));
@@ -1024,7 +1024,7 @@
 
   CreateService(ProfileSyncService::AUTO_START);
   SignIn();
-  EXPECT_CALL(*component_factory(), CreateSyncEngine(_, _, _))
+  EXPECT_CALL(*component_factory(), CreateSyncEngine(_, _, _, _))
       .WillOnce(
           Return(ByMove(std::make_unique<FakeSyncEngineCollectCredentials>(
               &init_account_id, base::RepeatingClosure()))));
@@ -1085,7 +1085,7 @@
 
   CreateService(ProfileSyncService::AUTO_START);
   SignIn();
-  EXPECT_CALL(*component_factory(), CreateSyncEngine(_, _, _))
+  EXPECT_CALL(*component_factory(), CreateSyncEngine(_, _, _, _))
       .WillOnce(
           Return(ByMove(std::make_unique<FakeSyncEngineCollectCredentials>(
               &init_account_id, base::RepeatingClosure()))));
@@ -1199,7 +1199,7 @@
           ReturnNewFakeDataTypeManager(GetDefaultConfigureCalledCallback()))
       .WillOnce(
           ReturnNewFakeDataTypeManager(GetDefaultConfigureCalledCallback()));
-  EXPECT_CALL(*component_factory(), CreateSyncEngine(_, _, _))
+  EXPECT_CALL(*component_factory(), CreateSyncEngine(_, _, _, _))
       .WillOnce(ReturnNewFakeSyncEngine())
       .WillOnce(ReturnNewFakeSyncEngine());
 
diff --git a/components/sync/driver/sync_api_component_factory.h b/components/sync/driver/sync_api_component_factory.h
index b0bfa73..fe6655cd 100644
--- a/components/sync/driver/sync_api_component_factory.h
+++ b/components/sync/driver/sync_api_component_factory.h
@@ -24,6 +24,7 @@
 class DataTypeManager;
 class DataTypeManagerObserver;
 class SyncEngine;
+class SyncInvalidationsService;
 class SyncPrefs;
 
 // This factory provides sync driver code with the model type specific sync/api
@@ -40,10 +41,16 @@
       ModelTypeConfigurer* configurer,
       DataTypeManagerObserver* observer) = 0;
 
-  // Creating this in the factory helps us mock it out in testing.
+  // Creating this in the factory helps us mock it out in testing. |invalidator|
+  // and |sync_invalidation_service| are different invalidations. SyncEngine
+  // handles incoming invalidations from both of them (if provided).
+  // |sync_invalidation_service| is a new sync-specific invalidations service
+  // and it may be nullptr if it is disabled or not supported. In future, there
+  // will leave only one invalidation service.
   virtual std::unique_ptr<SyncEngine> CreateSyncEngine(
       const std::string& name,
       invalidation::InvalidationService* invalidator,
+      syncer::SyncInvalidationsService* sync_invalidation_service,
       const base::WeakPtr<SyncPrefs>& sync_prefs) = 0;
 };
 
diff --git a/components/sync/driver/sync_api_component_factory_mock.h b/components/sync/driver/sync_api_component_factory_mock.h
index dc0108d0..b512623 100644
--- a/components/sync/driver/sync_api_component_factory_mock.h
+++ b/components/sync/driver/sync_api_component_factory_mock.h
@@ -32,10 +32,11 @@
                    const DataTypeEncryptionHandler*,
                    ModelTypeConfigurer*,
                    DataTypeManagerObserver*));
-  MOCK_METHOD3(CreateSyncEngine,
+  MOCK_METHOD4(CreateSyncEngine,
                std::unique_ptr<SyncEngine>(
                    const std::string& name,
                    invalidation::InvalidationService* invalidator,
+                   syncer::SyncInvalidationsService* sync_invalidations_service,
                    const base::WeakPtr<SyncPrefs>& sync_prefs));
 };
 
diff --git a/components/sync/driver/sync_client.h b/components/sync/driver/sync_client.h
index 2418822..320d7fb 100644
--- a/components/sync/driver/sync_client.h
+++ b/components/sync/driver/sync_client.h
@@ -29,6 +29,7 @@
 
 class SyncApiComponentFactory;
 class SyncableService;
+class SyncInvalidationsService;
 class SyncService;
 class SyncTypePreferenceProvider;
 class TrustedVaultClient;
@@ -60,6 +61,7 @@
       SyncService* sync_service) = 0;
 
   virtual invalidation::InvalidationService* GetInvalidationService() = 0;
+  virtual syncer::SyncInvalidationsService* GetSyncInvalidationsService() = 0;
   virtual TrustedVaultClient* GetTrustedVaultClient() = 0;
   virtual scoped_refptr<ExtensionsActivity> GetExtensionsActivity() = 0;
 
diff --git a/components/sync/driver/sync_client_mock.h b/components/sync/driver/sync_client_mock.h
index 68e64918..073219d 100644
--- a/components/sync/driver/sync_client_mock.h
+++ b/components/sync/driver/sync_client_mock.h
@@ -25,6 +25,8 @@
   MOCK_METHOD0(GetPasswordStateChangedCallback, base::RepeatingClosure());
 
   MOCK_METHOD0(GetInvalidationService, invalidation::InvalidationService*());
+  MOCK_METHOD0(GetSyncInvalidationsService,
+               syncer::SyncInvalidationsService*());
   MOCK_METHOD0(GetTrustedVaultClient, TrustedVaultClient*());
   MOCK_METHOD0(GetExtensionsActivity, scoped_refptr<ExtensionsActivity>());
   MOCK_METHOD1(GetSyncableServiceForType,
diff --git a/components/sync/invalidations/BUILD.gn b/components/sync/invalidations/BUILD.gn
index e689705..c2d903f 100644
--- a/components/sync/invalidations/BUILD.gn
+++ b/components/sync/invalidations/BUILD.gn
@@ -9,8 +9,11 @@
     "fcm_handler.cc",
     "fcm_handler.h",
     "invalidations_listener.h",
-    "sync_invalidations_service.cc",
+    "switches.cc",
+    "switches.h",
     "sync_invalidations_service.h",
+    "sync_invalidations_service_impl.cc",
+    "sync_invalidations_service_impl.h",
   ]
 
   public_deps = [
@@ -19,3 +22,15 @@
     "//components/keyed_service/core",
   ]
 }
+
+static_library("test_support") {
+  testonly = true
+  sources = [
+    "mock_sync_invalidations_service.cc",
+    "mock_sync_invalidations_service.h",
+  ]
+
+  public_deps = [ "//components/sync/invalidations" ]
+
+  deps = [ "//testing/gmock" ]
+}
diff --git a/components/sync/invalidations/fcm_handler.cc b/components/sync/invalidations/fcm_handler.cc
index faa3345..f08feb8 100644
--- a/components/sync/invalidations/fcm_handler.cc
+++ b/components/sync/invalidations/fcm_handler.cc
@@ -27,6 +27,7 @@
 
 FCMHandler::~FCMHandler() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(listeners_.empty());
   StopListening();
 }
 
diff --git a/components/sync/invalidations/fcm_handler_unittest.cc b/components/sync/invalidations/fcm_handler_unittest.cc
index 15625024..75c0649 100644
--- a/components/sync/invalidations/fcm_handler_unittest.cc
+++ b/components/sync/invalidations/fcm_handler_unittest.cc
@@ -120,6 +120,7 @@
 
   EXPECT_CALL(mock_listener, OnInvalidationReceived(kPayloadValue));
   fcm_handler_.OnMessage(kSyncInvalidationsAppId, gcm_message);
+  fcm_handler_.RemoveListener(&mock_listener);
 }
 
 }  // namespace
diff --git a/components/sync/invalidations/mock_sync_invalidations_service.cc b/components/sync/invalidations/mock_sync_invalidations_service.cc
new file mode 100644
index 0000000..07d1694
--- /dev/null
+++ b/components/sync/invalidations/mock_sync_invalidations_service.cc
@@ -0,0 +1,13 @@
+// Copyright 2020 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/sync/invalidations/mock_sync_invalidations_service.h"
+
+namespace syncer {
+
+MockSyncInvalidationsService::MockSyncInvalidationsService() = default;
+
+MockSyncInvalidationsService::~MockSyncInvalidationsService() = default;
+
+}  // namespace syncer
diff --git a/components/sync/invalidations/mock_sync_invalidations_service.h b/components/sync/invalidations/mock_sync_invalidations_service.h
new file mode 100644
index 0000000..fd98d07
--- /dev/null
+++ b/components/sync/invalidations/mock_sync_invalidations_service.h
@@ -0,0 +1,24 @@
+// Copyright 2020 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_SYNC_INVALIDATIONS_MOCK_SYNC_INVALIDATIONS_SERVICE_H_
+#define COMPONENTS_SYNC_INVALIDATIONS_MOCK_SYNC_INVALIDATIONS_SERVICE_H_
+
+#include "components/sync/invalidations/sync_invalidations_service.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace syncer {
+
+class MockSyncInvalidationsService : public SyncInvalidationsService {
+ public:
+  MockSyncInvalidationsService();
+  ~MockSyncInvalidationsService() override;
+
+  MOCK_METHOD(void, AddListener, (InvalidationsListener * listener));
+  MOCK_METHOD(void, RemoveListener, (InvalidationsListener * listener));
+};
+
+}  // namespace syncer
+
+#endif  // COMPONENTS_SYNC_INVALIDATIONS_MOCK_SYNC_INVALIDATIONS_SERVICE_H_
diff --git a/components/sync/invalidations/switches.cc b/components/sync/invalidations/switches.cc
new file mode 100644
index 0000000..1d923aaa
--- /dev/null
+++ b/components/sync/invalidations/switches.cc
@@ -0,0 +1,12 @@
+// Copyright 2020 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/sync/invalidations/switches.h"
+
+namespace switches {
+
+const base::Feature kSubscribeForSyncInvalidations = {
+    "SubscribeForSyncInvalidations", base::FEATURE_DISABLED_BY_DEFAULT};
+
+}  // namespace switches
diff --git a/components/sync/invalidations/switches.h b/components/sync/invalidations/switches.h
new file mode 100644
index 0000000..e4e68ef5
--- /dev/null
+++ b/components/sync/invalidations/switches.h
@@ -0,0 +1,16 @@
+// Copyright 2020 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_SYNC_INVALIDATIONS_SWITCHES_H_
+#define COMPONENTS_SYNC_INVALIDATIONS_SWITCHES_H_
+
+#include "base/feature_list.h"
+
+namespace switches {
+
+extern const base::Feature kSubscribeForSyncInvalidations;
+
+}  // namespace switches
+
+#endif  // COMPONENTS_SYNC_INVALIDATIONS_SWITCHES_H_
diff --git a/components/sync/invalidations/sync_invalidations_service.h b/components/sync/invalidations/sync_invalidations_service.h
index cd67811..46137d9 100644
--- a/components/sync/invalidations/sync_invalidations_service.h
+++ b/components/sync/invalidations/sync_invalidations_service.h
@@ -5,38 +5,23 @@
 #ifndef COMPONENTS_SYNC_INVALIDATIONS_SYNC_INVALIDATIONS_SERVICE_H_
 #define COMPONENTS_SYNC_INVALIDATIONS_SYNC_INVALIDATIONS_SERVICE_H_
 
-#include <memory>
-#include <string>
-
 #include "components/keyed_service/core/keyed_service.h"
 
-namespace gcm {
-class GCMDriver;
-}
-
-namespace instance_id {
-class InstanceIDDriver;
-}
-
 namespace syncer {
-class FCMHandler;
+class InvalidationsListener;
 
 // Service which is used to register with FCM. It is used to obtain an FCM token
 // which is used to send invalidations from the server. The service also
 // provides incoming invalidations handling and an interface to subscribe to
-// invalidations.
+// invalidations. To subscribe for invalidations a new InvalidationsListener
+// should be added.
 class SyncInvalidationsService : public KeyedService {
  public:
-  SyncInvalidationsService(gcm::GCMDriver* gcm_driver,
-                           instance_id::InstanceIDDriver* instance_id_driver,
-                           const std::string& sender_id,
-                           const std::string& app_id);
-  ~SyncInvalidationsService() override;
-
-  void Shutdown() override;
-
- private:
-  std::unique_ptr<FCMHandler> fcm_handler_;
+  // Add or remove a new listener which will be notified on each new incoming
+  // invalidation. |listener| must not be nullptr. If there is no such
+  // |listener| then RemoveListener will do nothing.
+  virtual void AddListener(InvalidationsListener* listener) = 0;
+  virtual void RemoveListener(InvalidationsListener* listener) = 0;
 };
 
 }  // namespace syncer
diff --git a/components/sync/invalidations/sync_invalidations_service.cc b/components/sync/invalidations/sync_invalidations_service_impl.cc
similarity index 60%
rename from components/sync/invalidations/sync_invalidations_service.cc
rename to components/sync/invalidations/sync_invalidations_service_impl.cc
index 35feba4..275a2ce7 100644
--- a/components/sync/invalidations/sync_invalidations_service.cc
+++ b/components/sync/invalidations/sync_invalidations_service_impl.cc
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/sync/invalidations/sync_invalidations_service.h"
+#include "components/sync/invalidations/sync_invalidations_service_impl.h"
 
 #include "components/sync/invalidations/fcm_handler.h"
 
 namespace syncer {
 
-SyncInvalidationsService::SyncInvalidationsService(
+SyncInvalidationsServiceImpl::SyncInvalidationsServiceImpl(
     gcm::GCMDriver* gcm_driver,
     instance_id::InstanceIDDriver* instance_id_driver,
     const std::string& sender_id,
@@ -18,9 +18,19 @@
   fcm_handler_->StartListening();
 }
 
-SyncInvalidationsService::~SyncInvalidationsService() = default;
+SyncInvalidationsServiceImpl::~SyncInvalidationsServiceImpl() = default;
 
-void SyncInvalidationsService::Shutdown() {
+void SyncInvalidationsServiceImpl::AddListener(
+    InvalidationsListener* listener) {
+  fcm_handler_->AddListener(listener);
+}
+
+void SyncInvalidationsServiceImpl::RemoveListener(
+    InvalidationsListener* listener) {
+  fcm_handler_->RemoveListener(listener);
+}
+
+void SyncInvalidationsServiceImpl::Shutdown() {
   fcm_handler_.reset();
 }
 
diff --git a/components/sync/invalidations/sync_invalidations_service_impl.h b/components/sync/invalidations/sync_invalidations_service_impl.h
new file mode 100644
index 0000000..75c1c7c
--- /dev/null
+++ b/components/sync/invalidations/sync_invalidations_service_impl.h
@@ -0,0 +1,48 @@
+// Copyright 2020 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_SYNC_INVALIDATIONS_SYNC_INVALIDATIONS_SERVICE_IMPL_H_
+#define COMPONENTS_SYNC_INVALIDATIONS_SYNC_INVALIDATIONS_SERVICE_IMPL_H_
+
+#include <memory>
+#include <string>
+
+#include "components/sync/invalidations/sync_invalidations_service.h"
+
+namespace gcm {
+class GCMDriver;
+}
+
+namespace instance_id {
+class InstanceIDDriver;
+}
+
+namespace syncer {
+class FCMHandler;
+class InvalidationsListener;
+
+// The non-test implementation of SyncInvalidationsService.
+class SyncInvalidationsServiceImpl : public SyncInvalidationsService {
+ public:
+  SyncInvalidationsServiceImpl(
+      gcm::GCMDriver* gcm_driver,
+      instance_id::InstanceIDDriver* instance_id_driver,
+      const std::string& sender_id,
+      const std::string& app_id);
+  ~SyncInvalidationsServiceImpl() override;
+
+  // SyncInvalidationsService implementation.
+  void AddListener(InvalidationsListener* listener) override;
+  void RemoveListener(InvalidationsListener* listener) override;
+
+  // KeyedService overrides.
+  void Shutdown() override;
+
+ private:
+  std::unique_ptr<FCMHandler> fcm_handler_;
+};
+
+}  // namespace syncer
+
+#endif  // COMPONENTS_SYNC_INVALIDATIONS_SYNC_INVALIDATIONS_SERVICE_IMPL_H_
diff --git a/ios/chrome/browser/sync/ios_chrome_sync_client.h b/ios/chrome/browser/sync/ios_chrome_sync_client.h
index 60ef4bf..00e5cb3 100644
--- a/ios/chrome/browser/sync/ios_chrome_sync_client.h
+++ b/ios/chrome/browser/sync/ios_chrome_sync_client.h
@@ -48,6 +48,7 @@
   syncer::DataTypeController::TypeVector CreateDataTypeControllers(
       syncer::SyncService* sync_service) override;
   invalidation::InvalidationService* GetInvalidationService() override;
+  syncer::SyncInvalidationsService* GetSyncInvalidationsService() override;
   syncer::TrustedVaultClient* GetTrustedVaultClient() override;
   BookmarkUndoService* GetBookmarkUndoService() override;
   scoped_refptr<syncer::ExtensionsActivity> GetExtensionsActivity() override;
diff --git a/ios/chrome/browser/sync/ios_chrome_sync_client.mm b/ios/chrome/browser/sync/ios_chrome_sync_client.mm
index d8182340..501150b 100644
--- a/ios/chrome/browser/sync/ios_chrome_sync_client.mm
+++ b/ios/chrome/browser/sync/ios_chrome_sync_client.mm
@@ -195,6 +195,12 @@
   return nullptr;
 }
 
+syncer::SyncInvalidationsService*
+IOSChromeSyncClient::GetSyncInvalidationsService() {
+  // TODO(crbug.com/1082122): implement sync invalidations on iOS.
+  return nullptr;
+}
+
 syncer::TrustedVaultClient* IOSChromeSyncClient::GetTrustedVaultClient() {
   return trusted_vault_client_.get();
 }
diff --git a/ios/web_view/internal/sync/web_view_sync_client.h b/ios/web_view/internal/sync/web_view_sync_client.h
index 4496116..42f300b 100644
--- a/ios/web_view/internal/sync/web_view_sync_client.h
+++ b/ios/web_view/internal/sync/web_view_sync_client.h
@@ -32,7 +32,8 @@
       signin::IdentityManager* identity_manager,
       syncer::ModelTypeStoreService* model_type_store_service,
       syncer::DeviceInfoSyncService* device_info_sync_service,
-      invalidation::InvalidationService* invalidation_service);
+      invalidation::InvalidationService* invalidation_service,
+      syncer::SyncInvalidationsService* sync_invalidations_service);
   ~WebViewSyncClient() override;
 
   // BrowserSyncClient implementation.
@@ -51,6 +52,7 @@
   syncer::DataTypeController::TypeVector CreateDataTypeControllers(
       syncer::SyncService* sync_service) override;
   invalidation::InvalidationService* GetInvalidationService() override;
+  syncer::SyncInvalidationsService* GetSyncInvalidationsService() override;
   syncer::TrustedVaultClient* GetTrustedVaultClient() override;
   BookmarkUndoService* GetBookmarkUndoService() override;
   scoped_refptr<syncer::ExtensionsActivity> GetExtensionsActivity() override;
@@ -73,6 +75,7 @@
   syncer::ModelTypeStoreService* model_type_store_service_;
   syncer::DeviceInfoSyncService* device_info_sync_service_;
   invalidation::InvalidationService* invalidation_service_;
+  syncer::SyncInvalidationsService* sync_invalidations_service_;
 
   // TODO(crbug.com/915154): Revert to SyncApiComponentFactory once common
   // controller creation is moved elsewhere.
diff --git a/ios/web_view/internal/sync/web_view_sync_client.mm b/ios/web_view/internal/sync/web_view_sync_client.mm
index 483bd6cf..700b76d 100644
--- a/ios/web_view/internal/sync/web_view_sync_client.mm
+++ b/ios/web_view/internal/sync/web_view_sync_client.mm
@@ -73,7 +73,9 @@
       WebViewDeviceInfoSyncServiceFactory::GetForBrowserState(browser_state),
       WebViewProfileInvalidationProviderFactory::GetForBrowserState(
           browser_state)
-          ->GetInvalidationService());
+          ->GetInvalidationService(),
+      /*sync_invalidations_service=*/nullptr);
+  // TODO(crbug.com/1082122): implement sync invalidations on iOS platform.
 }
 
 WebViewSyncClient::WebViewSyncClient(
@@ -85,7 +87,8 @@
     signin::IdentityManager* identity_manager,
     syncer::ModelTypeStoreService* model_type_store_service,
     syncer::DeviceInfoSyncService* device_info_sync_service,
-    invalidation::InvalidationService* invalidation_service)
+    invalidation::InvalidationService* invalidation_service,
+    syncer::SyncInvalidationsService* sync_invalidations_service)
     : profile_web_data_service_(profile_web_data_service),
       account_web_data_service_(account_web_data_service),
       profile_password_store_(profile_password_store),
@@ -94,7 +97,8 @@
       identity_manager_(identity_manager),
       model_type_store_service_(model_type_store_service),
       device_info_sync_service_(device_info_sync_service),
-      invalidation_service_(invalidation_service) {
+      invalidation_service_(invalidation_service),
+      sync_invalidations_service_(sync_invalidations_service) {
   component_factory_ =
       std::make_unique<browser_sync::ProfileSyncComponentsFactoryImpl>(
           this, version_info::Channel::STABLE,
@@ -170,6 +174,11 @@
   return invalidation_service_;
 }
 
+syncer::SyncInvalidationsService*
+WebViewSyncClient::GetSyncInvalidationsService() {
+  return sync_invalidations_service_;
+}
+
 syncer::TrustedVaultClient* WebViewSyncClient::GetTrustedVaultClient() {
   return nullptr;
 }
diff --git a/ios/web_view/internal/sync/web_view_sync_client_unittest.mm b/ios/web_view/internal/sync/web_view_sync_client_unittest.mm
index 98f35f6..443136e 100644
--- a/ios/web_view/internal/sync/web_view_sync_client_unittest.mm
+++ b/ios/web_view/internal/sync/web_view_sync_client_unittest.mm
@@ -56,7 +56,8 @@
                 identity_test_environment_.identity_manager(),
                 &model_type_store_service_,
                 &device_info_sync_service_,
-                &invalidation_service_) {
+                &invalidation_service_,
+                /*sync_invalidations_service=*/nullptr) {
     pref_service_.registry()->RegisterBooleanPref(
         prefs::kSavingBrowserHistoryDisabled, true);
     pref_service_.registry()->RegisterDictionaryPref(