[GCM] Make GCMProfileService create and own GCMClient

We're switching to one GCMClient per profile.

BUG=284553
TEST=existing tests

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@249824 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/services/gcm/gcm_client_factory.cc b/chrome/browser/services/gcm/gcm_client_factory.cc
index f928393..ef78865 100644
--- a/chrome/browser/services/gcm/gcm_client_factory.cc
+++ b/chrome/browser/services/gcm/gcm_client_factory.cc
@@ -4,139 +4,12 @@
 
 #include "chrome/browser/services/gcm/gcm_client_factory.h"
 
-#include "base/files/file_path.h"
-#include "base/lazy_instance.h"
-#include "base/logging.h"
-#include "base/path_service.h"
-#include "base/threading/sequenced_worker_pool.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/io_thread.h"
-#include "chrome/common/chrome_constants.h"
-#include "chrome/common/chrome_paths.h"
-#include "chrome/common/chrome_version_info.h"
-#include "content/public/browser/browser_thread.h"
 #include "google_apis/gcm/gcm_client_impl.h"
-#include "net/url_request/url_request_context_getter.h"
 
 namespace gcm {
 
-namespace {
-
-static bool g_gcm_client_io_task_posted = false;
-static base::LazyInstance<GCMClientImpl>::Leaky g_gcm_client =
-    LAZY_INSTANCE_INITIALIZER;
-static GCMClientFactory::TestingFactoryFunction g_gcm_client_factory = NULL;
-static GCMClient* g_gcm_client_override = NULL;
-
-checkin_proto::ChromeBuildProto_Platform GetPlatform() {
-#if defined(OS_WIN)
-  return checkin_proto::ChromeBuildProto_Platform_PLATFORM_WIN;
-#elif defined(OS_MACOSX)
-  return checkin_proto::ChromeBuildProto_Platform_PLATFORM_MAC;
-#elif defined(OS_CHROMEOS)
-  return checkin_proto::ChromeBuildProto_Platform_PLATFORM_CROS;
-#elif defined(OS_LINUX)
-  return checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
-#else
-  // For all other platforms, return as LINUX.
-  return checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
-#endif
-}
-
-std::string GetVersion() {
-  chrome::VersionInfo version_info;
-  return version_info.Version();
-}
-
-checkin_proto::ChromeBuildProto_Channel GetChannel() {
-  chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
-  switch (channel) {
-    case chrome::VersionInfo::CHANNEL_UNKNOWN:
-      return checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN;
-    case chrome::VersionInfo::CHANNEL_CANARY:
-      return checkin_proto::ChromeBuildProto_Channel_CHANNEL_CANARY;
-    case chrome::VersionInfo::CHANNEL_DEV:
-      return checkin_proto::ChromeBuildProto_Channel_CHANNEL_DEV;
-    case chrome::VersionInfo::CHANNEL_BETA:
-      return checkin_proto::ChromeBuildProto_Channel_CHANNEL_BETA;
-    case chrome::VersionInfo::CHANNEL_STABLE:
-      return checkin_proto::ChromeBuildProto_Channel_CHANNEL_STABLE;
-    default:
-      NOTREACHED();
-      return checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN;
-  };
-}
-
-void BuildClientFromIO(const scoped_refptr<net::URLRequestContextGetter>&
-    url_request_context_getter) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
-  DCHECK(g_gcm_client == NULL);
-
-  checkin_proto::ChromeBuildProto chrome_build_proto;
-  chrome_build_proto.set_platform(GetPlatform());
-  chrome_build_proto.set_chrome_version(GetVersion());
-  chrome_build_proto.set_channel(GetChannel());
-
-  base::FilePath gcm_store_path;
-  CHECK(PathService::Get(chrome::DIR_USER_DATA, &gcm_store_path));
-  gcm_store_path = gcm_store_path.Append(chrome::kGCMStoreDirname);
-
-  scoped_refptr<base::SequencedWorkerPool> worker_pool(
-      content::BrowserThread::GetBlockingPool());
-  scoped_refptr<base::SequencedTaskRunner> blocking_task_runner(
-      worker_pool->GetSequencedTaskRunnerWithShutdownBehavior(
-          worker_pool->GetSequenceToken(),
-          base::SequencedWorkerPool::SKIP_ON_SHUTDOWN));
-
-  g_gcm_client.Pointer()->Initialize(chrome_build_proto,
-                                     gcm_store_path,
-                                     blocking_task_runner,
-                                     url_request_context_getter);
-}
-
-}  // namespace
-
-// static
-void GCMClientFactory::BuildClientFromUI() {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-
-  if (g_gcm_client_factory) {
-    if (!g_gcm_client_override)
-      g_gcm_client_override = g_gcm_client_factory();
-    return;
-  }
-
-  if (g_gcm_client_io_task_posted)
-    return;
-  g_gcm_client_io_task_posted = true;
-
-  // system_url_request_context_getter can only be retrieved from
-  // UI thread.
-  scoped_refptr<net::URLRequestContextGetter> url_request_context_getter(
-      g_browser_process->io_thread()->system_url_request_context_getter());
-
-  content::BrowserThread::PostTask(
-      content::BrowserThread::IO,
-      FROM_HERE,
-      base::Bind(&BuildClientFromIO, url_request_context_getter));
-}
-
-// static
-GCMClient* GCMClientFactory::GetClient() {
-  // TODO(jianli): Make test code also get the client on IO thread.
-  if (g_gcm_client_override)
-    return g_gcm_client_override;
-
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
-  DCHECK(!(g_gcm_client == NULL));
-
-  return g_gcm_client.Pointer();
-}
-
-// static
-void GCMClientFactory::SetTestingFactory(TestingFactoryFunction factory) {
-  g_gcm_client_factory = factory;
-  BuildClientFromUI();
+scoped_ptr<GCMClient> GCMClientFactory::BuildInstance() {
+  return scoped_ptr<GCMClient>(new GCMClientImpl());
 }
 
 GCMClientFactory::GCMClientFactory() {
diff --git a/chrome/browser/services/gcm/gcm_client_factory.h b/chrome/browser/services/gcm/gcm_client_factory.h
index e2914f0..9112399 100644
--- a/chrome/browser/services/gcm/gcm_client_factory.h
+++ b/chrome/browser/services/gcm/gcm_client_factory.h
@@ -6,11 +6,7 @@
 #define CHROME_BROWSER_SERVICES_GCM_GCM_CLIENT_FACTORY_H_
 
 #include "base/macros.h"
-#include "base/memory/ref_counted.h"
-
-namespace net {
-class URLRequestContextGetter;
-}
+#include "base/memory/scoped_ptr.h"
 
 namespace gcm {
 
@@ -18,22 +14,14 @@
 
 class GCMClientFactory {
  public:
-  // Creates the single instance of GCMClient if not yet created.
-  // Called on UI thread.
-  static void  BuildClientFromUI();
+  GCMClientFactory();
+  virtual ~GCMClientFactory();
 
-  // Returns a single instance of GCMClient.
-  // This should be called on IO thread.
-  static GCMClient* GetClient();
-
-  // Passes a mocked instance for testing purpose.
-  typedef GCMClient* (*TestingFactoryFunction)();
-  static void SetTestingFactory(TestingFactoryFunction factory);
+  // Creates a new instance of GCMClient. The testing code could override this
+  // to provide a mocked instance.
+  virtual scoped_ptr<GCMClient> BuildInstance();
 
  private:
-  GCMClientFactory();
-  ~GCMClientFactory();
-
   DISALLOW_COPY_AND_ASSIGN(GCMClientFactory);
 };
 
diff --git a/chrome/browser/services/gcm/gcm_client_mock.cc b/chrome/browser/services/gcm/gcm_client_mock.cc
index e2e3bc3..4945a27b 100644
--- a/chrome/browser/services/gcm/gcm_client_mock.cc
+++ b/chrome/browser/services/gcm/gcm_client_mock.cc
@@ -25,14 +25,22 @@
 
 }  // namespace
 
-GCMClientMock::GCMClientMock()
-    : ready_(true),
+GCMClientMock::GCMClientMock(Status status)
+    : status_(status),
       simulate_server_error_(false) {
 }
 
 GCMClientMock::~GCMClientMock() {
 }
 
+void GCMClientMock::Initialize(
+    const checkin_proto::ChromeBuildProto& chrome_build_proto,
+    const base::FilePath& store_path,
+    const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner,
+    const scoped_refptr<net::URLRequestContextGetter>&
+        url_request_context_getter) {
+}
+
 void GCMClientMock::SetUserDelegate(const std::string& username,
                                     Delegate* delegate) {
   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
@@ -98,7 +106,7 @@
 }
 
 bool GCMClientMock::IsReady() const {
-  return ready_;
+  return status_ == READY;
 }
 
 void GCMClientMock::ReceiveMessage(const std::string& username,
@@ -129,15 +137,11 @@
                  app_id));
 }
 
-void GCMClientMock::SetReady(bool ready) {
+void GCMClientMock::SetReady() {
   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+  DCHECK_EQ(status_, NOT_READY);
 
-  if (ready == ready_)
-    return;
-  ready_ = ready;
-
-  if (!ready_)
-    return;
+  status_ = READY;
   content::BrowserThread::PostTask(
       content::BrowserThread::IO,
       FROM_HERE,
diff --git a/chrome/browser/services/gcm/gcm_client_mock.h b/chrome/browser/services/gcm/gcm_client_mock.h
index f15b5f0..62397ee 100644
--- a/chrome/browser/services/gcm/gcm_client_mock.h
+++ b/chrome/browser/services/gcm/gcm_client_mock.h
@@ -14,11 +14,22 @@
 
 class GCMClientMock : public GCMClient {
  public:
-  GCMClientMock();
+  enum Status {
+    NOT_READY,
+    READY
+  };
+
+  explicit GCMClientMock(Status status);
   virtual ~GCMClientMock();
 
   // Overridden from GCMClient:
   // Called on IO thread.
+  virtual void Initialize(
+      const checkin_proto::ChromeBuildProto& chrome_build_proto,
+      const base::FilePath& store_path,
+      const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner,
+      const scoped_refptr<net::URLRequestContextGetter>&
+          url_request_context_getter) OVERRIDE;
   virtual void SetUserDelegate(const std::string& username,
                                Delegate* delegate) OVERRIDE;
   virtual void CheckIn(const std::string& username) OVERRIDE;
@@ -45,7 +56,8 @@
     simulate_server_error_ = simulate_server_error;
   }
 
-  void SetReady(bool ready);
+  // Can only transition from non-ready to ready.
+  void SetReady();
 
   static CheckinInfo GetCheckinInfoFromUsername(const std::string& username);
   static std::string GetRegistrationIdFromSenderIds(
@@ -74,7 +86,7 @@
 
   std::map<std::string, Delegate*> delegates_;
 
-  bool ready_;
+  Status status_;
 
   // The testing code could set this to simulate the server error in order to
   // test the error scenario.
diff --git a/chrome/browser/services/gcm/gcm_profile_service.cc b/chrome/browser/services/gcm/gcm_profile_service.cc
index 731a699..5740892 100644
--- a/chrome/browser/services/gcm/gcm_profile_service.cc
+++ b/chrome/browser/services/gcm/gcm_profile_service.cc
@@ -5,9 +5,12 @@
 #include "chrome/browser/services/gcm/gcm_profile_service.h"
 
 #include "base/base64.h"
+#include "base/files/file_path.h"
 #include "base/logging.h"
+#include "base/path_service.h"
 #include "base/prefs/pref_service.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/threading/sequenced_worker_pool.h"
 #include "chrome/browser/chrome_notification_types.h"
 #if !defined(OS_ANDROID)
 #include "chrome/browser/extensions/api/gcm/gcm_api.h"
@@ -18,6 +21,8 @@
 #include "chrome/browser/services/gcm/gcm_event_router.h"
 #include "chrome/browser/signin/signin_manager.h"
 #include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_version_info.h"
 #include "chrome/common/pref_names.h"
 #include "components/user_prefs/pref_registry_syncable.h"
@@ -27,15 +32,60 @@
 #include "content/public/browser/notification_source.h"
 #include "extensions/browser/extension_system.h"
 #include "extensions/common/extension.h"
+#include "google_apis/gcm/protocol/android_checkin.pb.h"
+#include "net/url_request/url_request_context_getter.h"
 
 using extensions::Extension;
 
 namespace gcm {
 
+namespace {
+
 const char kRegistrationKey[] = "gcm.registration";
 const char kSendersKey[] = "senders";
 const char kRegistrationIDKey[] = "reg_id";
 
+checkin_proto::ChromeBuildProto_Platform GetPlatform() {
+#if defined(OS_WIN)
+  return checkin_proto::ChromeBuildProto_Platform_PLATFORM_WIN;
+#elif defined(OS_MACOSX)
+  return checkin_proto::ChromeBuildProto_Platform_PLATFORM_MAC;
+#elif defined(OS_CHROMEOS)
+  return checkin_proto::ChromeBuildProto_Platform_PLATFORM_CROS;
+#elif defined(OS_LINUX)
+  return checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
+#else
+  // For all other platforms, return as LINUX.
+  return checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
+#endif
+}
+
+std::string GetVersion() {
+  chrome::VersionInfo version_info;
+  return version_info.Version();
+}
+
+checkin_proto::ChromeBuildProto_Channel GetChannel() {
+  chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
+  switch (channel) {
+    case chrome::VersionInfo::CHANNEL_UNKNOWN:
+      return checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN;
+    case chrome::VersionInfo::CHANNEL_CANARY:
+      return checkin_proto::ChromeBuildProto_Channel_CHANNEL_CANARY;
+    case chrome::VersionInfo::CHANNEL_DEV:
+      return checkin_proto::ChromeBuildProto_Channel_CHANNEL_DEV;
+    case chrome::VersionInfo::CHANNEL_BETA:
+      return checkin_proto::ChromeBuildProto_Channel_CHANNEL_BETA;
+    case chrome::VersionInfo::CHANNEL_STABLE:
+      return checkin_proto::ChromeBuildProto_Channel_CHANNEL_STABLE;
+    default:
+      NOTREACHED();
+      return checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN;
+  };
+}
+
+}  // namespace
+
 // Helper class to save tasks to run until we're ready to execute them.
 class GCMProfileService::DelayedTaskController {
  public:
@@ -203,7 +253,11 @@
   virtual void OnGCMReady() OVERRIDE;
 
   // Called on IO thread.
-  void Initialize();
+  void Initialize(
+      scoped_ptr<GCMClient> gcm_client,
+      const base::FilePath& store_path,
+      const scoped_refptr<net::URLRequestContextGetter>&
+          url_request_context_getter);
   void SetUser(const std::string& username);
   void RemoveUser();
   void CheckIn();
@@ -223,8 +277,7 @@
 
   const base::WeakPtr<GCMProfileService> service_;
 
-  // Not owned.
-  GCMClient* gcm_client_;
+  scoped_ptr<GCMClient> gcm_client_;
 
   // The username (email address) of the signed-in user.
   std::string username_;
@@ -236,18 +289,38 @@
 
 GCMProfileService::IOWorker::IOWorker(
     const base::WeakPtr<GCMProfileService>& service)
-    : service_(service),
-      gcm_client_(NULL) {
+    : service_(service) {
   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
 }
 
 GCMProfileService::IOWorker::~IOWorker() {
 }
 
-void GCMProfileService::IOWorker::Initialize() {
+void GCMProfileService::IOWorker::Initialize(
+    scoped_ptr<GCMClient> gcm_client,
+    const base::FilePath& store_path,
+    const scoped_refptr<net::URLRequestContextGetter>&
+        url_request_context_getter) {
   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
 
-  gcm_client_ = GCMClientFactory::GetClient();
+  gcm_client_ = gcm_client.Pass();
+
+  checkin_proto::ChromeBuildProto chrome_build_proto;
+  chrome_build_proto.set_platform(GetPlatform());
+  chrome_build_proto.set_chrome_version(GetVersion());
+  chrome_build_proto.set_channel(GetChannel());
+
+  scoped_refptr<base::SequencedWorkerPool> worker_pool(
+      content::BrowserThread::GetBlockingPool());
+  scoped_refptr<base::SequencedTaskRunner> blocking_task_runner(
+      worker_pool->GetSequencedTaskRunnerWithShutdownBehavior(
+          worker_pool->GetSequenceToken(),
+          base::SequencedWorkerPool::SKIP_ON_SHUTDOWN));
+
+  gcm_client_->Initialize(chrome_build_proto,
+                          store_path,
+                          blocking_task_runner,
+                          url_request_context_getter);
 
   content::BrowserThread::PostTask(
       content::BrowserThread::UI,
@@ -482,7 +555,7 @@
                  io_worker_));
 }
 
-void GCMProfileService::Initialize() {
+void GCMProfileService::Initialize(GCMClientFactory* gcm_client_factory) {
   delayed_task_controller_.reset(new DelayedTaskController);
 
   // This has to be done first since CheckIn depends on it.
@@ -492,12 +565,20 @@
   js_event_router_.reset(new extensions::GcmJsEventRouter(profile_));
 #endif
 
+  scoped_ptr<GCMClient> gcm_client(gcm_client_factory->BuildInstance());
+  scoped_refptr<net::URLRequestContextGetter> url_request_context_getter =
+      profile_->GetRequestContext();
+
   // This initializes GCMClient and also does the check to find out if GCMClient
   // is ready.
   content::BrowserThread::PostTask(
       content::BrowserThread::IO,
       FROM_HERE,
-      base::Bind(&GCMProfileService::IOWorker::Initialize, io_worker_));
+      base::Bind(&GCMProfileService::IOWorker::Initialize,
+                 io_worker_,
+                 base::Passed(&gcm_client),
+                 profile_->GetPath().Append(chrome::kGCMStoreDirname),
+                 url_request_context_getter));
 
   // In case that the profile has been signed in before GCMProfileService is
   // created.
diff --git a/chrome/browser/services/gcm/gcm_profile_service.h b/chrome/browser/services/gcm/gcm_profile_service.h
index ba87f307..ff6c511c 100644
--- a/chrome/browser/services/gcm/gcm_profile_service.h
+++ b/chrome/browser/services/gcm/gcm_profile_service.h
@@ -34,6 +34,7 @@
 
 namespace gcm {
 
+class GCMClientFactory;
 class GCMEventRouter;
 class GCMProfileServiceTestConsumer;
 
@@ -63,7 +64,7 @@
   explicit GCMProfileService(Profile* profile);
   virtual ~GCMProfileService();
 
-  void Initialize();
+  void Initialize(GCMClientFactory* gcm_client_factory);
 
   // Registers |sender_id| for an app. A registration ID will be returned by
   // the GCM server.
diff --git a/chrome/browser/services/gcm/gcm_profile_service_factory.cc b/chrome/browser/services/gcm/gcm_profile_service_factory.cc
index c116c1af..71e5546 100644
--- a/chrome/browser/services/gcm/gcm_profile_service_factory.cc
+++ b/chrome/browser/services/gcm/gcm_profile_service_factory.cc
@@ -37,13 +37,12 @@
 
 BrowserContextKeyedService* GCMProfileServiceFactory::BuildServiceInstanceFor(
     content::BrowserContext* context) const {
-  GCMClientFactory::BuildClientFromUI();
-
   Profile* profile = static_cast<Profile*>(context);
   if (!gcm::GCMProfileService::IsGCMEnabled(profile))
     return NULL;
   GCMProfileService* service = new GCMProfileService(profile);
-  service->Initialize();
+  GCMClientFactory gcm_client_factory;
+  service->Initialize(&gcm_client_factory);
   return service;
 }
 
diff --git a/chrome/browser/services/gcm/gcm_profile_service_unittest.cc b/chrome/browser/services/gcm/gcm_profile_service_unittest.cc
index 5a8f9ad..96310b2 100644
--- a/chrome/browser/services/gcm/gcm_profile_service_unittest.cc
+++ b/chrome/browser/services/gcm/gcm_profile_service_unittest.cc
@@ -7,7 +7,6 @@
 
 #include "base/bind.h"
 #include "base/command_line.h"
-#include "base/debug/leak_annotations.h"
 #include "base/message_loop/message_loop.h"
 #include "base/prefs/pref_service.h"
 #include "base/run_loop.h"
@@ -207,6 +206,31 @@
   GCMClient::Result send_error_result_;
 };
 
+class FakeGCMClientFactory : public GCMClientFactory {
+ public:
+  explicit FakeGCMClientFactory(GCMClientMock::Status gcm_client_initial_status)
+      : gcm_client_initial_status_(gcm_client_initial_status),
+        gcm_client_(NULL) {
+  }
+
+  virtual ~FakeGCMClientFactory() {
+  }
+
+  virtual scoped_ptr<GCMClient> BuildInstance() OVERRIDE {
+    DCHECK(!gcm_client_);
+    gcm_client_ = new GCMClientMock(gcm_client_initial_status_);
+    return scoped_ptr<GCMClient>(gcm_client_);
+  }
+
+  GCMClientMock* gcm_client() const { return gcm_client_; }
+
+ private:
+  GCMClientMock::Status gcm_client_initial_status_;
+  GCMClientMock* gcm_client_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeGCMClientFactory);
+};
+
 class GCMProfileServiceTestConsumer : public GCMProfileService::TestingDelegate{
  public:
   static BrowserContextKeyedService* BuildFakeSigninManager(
@@ -223,6 +247,7 @@
       : waiter_(waiter),
         extension_service_(NULL),
         signin_manager_(NULL),
+        gcm_client_initial_status_(GCMClientMock::READY),
         registration_result_(GCMClient::SUCCESS),
         has_persisted_registration_info_(false),
         send_result_(GCMClient::SUCCESS) {
@@ -299,7 +324,9 @@
         GCMProfileServiceFactory::GetInstance()->SetTestingFactoryAndUse(
             profile(), &GCMProfileServiceTestConsumer::BuildGCMProfileService));
     gcm_profile_service->set_testing_delegate(this);
-    gcm_profile_service->Initialize();
+    gcm_client_factory_.reset(
+        new FakeGCMClientFactory(gcm_client_initial_status_));
+    gcm_profile_service->Initialize(gcm_client_factory_.get());
   }
 
   void CheckIn(const std::string& username) {
@@ -371,6 +398,10 @@
     return GCMProfileServiceFactory::GetForProfile(profile());
   }
 
+  GCMClientMock* GetGCMClient() const {
+    return gcm_client_factory_->gcm_client();
+  }
+
   const std::string& GetUsername() const {
     return GetGCMProfileService()->username_;
   }
@@ -385,6 +416,10 @@
     return gcm_event_router_.get();
   }
 
+  void set_gcm_client_initial_status(GCMClientMock::Status status) {
+    gcm_client_initial_status_ = status;
+  }
+
   GCMClient::CheckinInfo checkin_info() const { return checkin_info_; }
   const std::string& registration_id() const { return registration_id_; }
   GCMClient::Result registration_result() const { return registration_result_; }
@@ -418,6 +453,9 @@
   ExtensionService* extension_service_;  // Not owned.
   FakeSigninManager* signin_manager_;  // Not owned.
   scoped_ptr<FakeGCMEventRouter> gcm_event_router_;
+  scoped_ptr<FakeGCMClientFactory> gcm_client_factory_;
+
+  GCMClientMock::Status gcm_client_initial_status_;
 
   GCMClient::CheckinInfo checkin_info_;
 
@@ -433,11 +471,6 @@
 
 class GCMProfileServiceTest : public testing::Test {
  public:
-  static GCMClient* BuildGCMClient() {
-    ANNOTATE_SCOPED_MEMORY_LEAK;
-    return new GCMClientMock();
-  }
-
   GCMProfileServiceTest() {
   }
 
@@ -461,9 +494,6 @@
     Encryptor::UseMockKeychain(true);
 #endif
 
-    // Mock a GCMClient.
-    GCMClientFactory::SetTestingFactory(&GCMProfileServiceTest::BuildGCMClient);
-
     // Create a main profile consumer.
     consumer_.reset(new GCMProfileServiceTestConsumer(&waiter_));
   }
@@ -508,21 +538,21 @@
   DISALLOW_COPY_AND_ASSIGN(GCMProfileServiceTest);
 };
 
-static GCMClientMock* GetGCMClientMock() {
-  return static_cast<GCMClientMock*>(GCMClientFactory::GetClient());
-}
-
 class ScopedGCMClientMockSimulateServerError {
  public:
-  ScopedGCMClientMockSimulateServerError() {
-    GetGCMClientMock()->set_simulate_server_error(true);
+  ScopedGCMClientMockSimulateServerError(
+      GCMProfileServiceTestConsumer* consumer)
+      : consumer_(consumer) {
+    consumer_->GetGCMClient()->set_simulate_server_error(true);
   }
 
   ~ScopedGCMClientMockSimulateServerError() {
-    GetGCMClientMock()->set_simulate_server_error(false);
+    consumer_->GetGCMClient()->set_simulate_server_error(false);
   }
 
  private:
+  GCMProfileServiceTestConsumer* consumer_;
+
   DISALLOW_COPY_AND_ASSIGN(ScopedGCMClientMockSimulateServerError);
 };
 
@@ -629,7 +659,8 @@
 
   // Check-in should not reach the server. Forcing GCMClient server error should
   // help catch this.
-  ScopedGCMClientMockSimulateServerError gcm_client_simulate_server_error;
+  ScopedGCMClientMockSimulateServerError gcm_client_simulate_server_error(
+      consumer());
 
   // Recreate GCMProfileService to test reading the check-in info from the
   // prefs store.
@@ -787,7 +818,8 @@
 
   // Register should not reach the server. Forcing GCMClient server error should
   // help catch this.
-  ScopedGCMClientMockSimulateServerError gcm_client_simulate_server_error;
+  ScopedGCMClientMockSimulateServerError gcm_client_simulate_server_error(
+      consumer());
 
   // This should read the registration info from the extension's state store.
   // We still need to wait since the reading from state store might happen at
@@ -815,8 +847,8 @@
   // preparation to call register 2nd time.
   consumer()->clear_registration_result();
 
-  // Mark that GCMClient is not ready.
-  GetGCMClientMock()->SetReady(false);
+  // Needs to create a GCMClient instance that is not ready initiallly.
+  consumer()->set_gcm_client_initial_status(GCMClientMock::NOT_READY);
 
   // Simulate start-up by recreating GCMProfileService.
   consumer()->CreateGCMProfileServiceInstance();
@@ -832,7 +864,7 @@
   EXPECT_EQ(GCMClient::UNKNOWN_ERROR, consumer()->registration_result());
 
   // Register operation will be invoked after GCMClient becomes ready.
-  GetGCMClientMock()->SetReady(true);
+  consumer()->GetGCMClient()->SetReady();
   WaitUntilCompleted();
   EXPECT_EQ(old_registration_id, consumer()->registration_id());
   EXPECT_EQ(GCMClient::SUCCESS, consumer()->registration_result());
@@ -933,7 +965,8 @@
   GCMClient::IncomingMessage message;
   message.data["key1"] = "value1";
   message.data["key2"] = "value2";
-  GetGCMClientMock()->ReceiveMessage(kTestingUsername, kTestingAppId, message);
+  consumer()->GetGCMClient()->ReceiveMessage(
+      kTestingUsername, kTestingAppId, message);
   WaitUntilCompleted();
   EXPECT_EQ(FakeGCMEventRouter::MESSAGE_EVENT,
             consumer()->gcm_event_router()->received_event());
@@ -948,7 +981,8 @@
   GCMClient::IncomingMessage message;
   message.data["key1"] = "value1";
   message.data["key2"] = "value2";
-  GetGCMClientMock()->ReceiveMessage(kTestingUsername, kTestingAppId, message);
+  consumer()->GetGCMClient()->ReceiveMessage(
+      kTestingUsername, kTestingAppId, message);
   PumpIOLoop();
 
   EXPECT_EQ(FakeGCMEventRouter::NO_EVENT,
@@ -957,7 +991,7 @@
 }
 
 TEST_F(GCMProfileServiceSingleProfileTest, MessagesDeleted) {
-  GetGCMClientMock()->DeleteMessages(kTestingUsername, kTestingAppId);
+  consumer()->GetGCMClient()->DeleteMessages(kTestingUsername, kTestingAppId);
   WaitUntilCompleted();
   EXPECT_EQ(FakeGCMEventRouter::MESSAGES_DELETED_EVENT,
             consumer()->gcm_event_router()->received_event());
@@ -968,7 +1002,7 @@
   // This will trigger check-out.
   consumer()->signin_manager()->SignOut();
 
-  GetGCMClientMock()->DeleteMessages(kTestingUsername, kTestingAppId);
+  consumer()->GetGCMClient()->DeleteMessages(kTestingUsername, kTestingAppId);
   PumpIOLoop();
 
   EXPECT_EQ(FakeGCMEventRouter::NO_EVENT,
@@ -1107,12 +1141,13 @@
   GCMClient::IncomingMessage message;
   message.data["key1"] = "value1";
   message.data["key2"] = "value2";
-  GetGCMClientMock()->ReceiveMessage(kTestingUsername, kTestingAppId, message);
+  consumer()->GetGCMClient()->ReceiveMessage(
+      kTestingUsername, kTestingAppId, message);
 
   // Trigger an incoming message for the same app in another profile.
   GCMClient::IncomingMessage message2;
   message2.data["foo"] = "bar";
-  GetGCMClientMock()->ReceiveMessage(
+  consumer2()->GetGCMClient()->ReceiveMessage(
       kTestingUsername2, kTestingAppId, message2);
 
   WaitUntilCompleted();
@@ -1132,7 +1167,7 @@
   GCMClient::IncomingMessage message3;
   message3.data["bar1"] = "foo1";
   message3.data["bar2"] = "foo2";
-  GetGCMClientMock()->ReceiveMessage(
+  consumer2()->GetGCMClient()->ReceiveMessage(
       kTestingUsername2, kTestingAppId2, message3);
 
   WaitUntilCompleted();
@@ -1203,7 +1238,7 @@
   GCMClient::IncomingMessage in_message;
   in_message.data["in1"] = "in_data1";
   in_message.data["in1_2"] = "in_data1_2";
-  GetGCMClientMock()->ReceiveMessage(
+  consumer()->GetGCMClient()->ReceiveMessage(
       kTestingUsername, kTestingAppId, in_message);
 
   WaitUntilCompleted();
@@ -1218,13 +1253,13 @@
   // profile.
   GCMClient::IncomingMessage in_message2;
   in_message2.data["in2"] = "in_data2";
-  GetGCMClientMock()->ReceiveMessage(
+  consumer2()->GetGCMClient()->ReceiveMessage(
       kTestingUsername2, kTestingAppId2, in_message2);
 
   GCMClient::IncomingMessage in_message3;
   in_message3.data["in3"] = "in_data3";
   in_message3.data["in3_2"] = "in_data3_2";
-  GetGCMClientMock()->ReceiveMessage(
+  consumer2()->GetGCMClient()->ReceiveMessage(
       kTestingUsername2, kTestingAppId, in_message3);
 
   consumer2()->gcm_event_router()->clear_results();
@@ -1273,7 +1308,7 @@
   // Incoming message will be ingored for the signed-out profile.
   GCMClient::IncomingMessage in_message4;
   in_message4.data["in4"] = "in_data4";
-  GetGCMClientMock()->ReceiveMessage(
+  consumer()->GetGCMClient()->ReceiveMessage(
       kTestingUsername, kTestingAppId, in_message4);
 
   consumer()->gcm_event_router()->clear_results();
@@ -1284,7 +1319,7 @@
   EXPECT_TRUE(consumer()->gcm_event_router()->app_id().empty());
 
   // Deleted messages event will be ingored for the signed-out profile.
-  GetGCMClientMock()->DeleteMessages(kTestingUsername, kTestingAppId);
+  consumer()->GetGCMClient()->DeleteMessages(kTestingUsername, kTestingAppId);
 
   PumpIOLoop();
 
@@ -1293,7 +1328,8 @@
   EXPECT_TRUE(consumer()->gcm_event_router()->app_id().empty());
 
   // Deleted messages event will go through for another signed-in profile.
-  GetGCMClientMock()->DeleteMessages(kTestingUsername2, kTestingAppId2);
+  consumer2()->GetGCMClient()->DeleteMessages(
+      kTestingUsername2, kTestingAppId2);
 
   consumer2()->gcm_event_router()->clear_results();
   WaitUntilCompleted();
@@ -1327,7 +1363,7 @@
   WaitUntilCompleted();
 
   // Incoming message will still be ingored for the signed-out user.
-  GetGCMClientMock()->ReceiveMessage(
+  consumer()->GetGCMClient()->ReceiveMessage(
       kTestingUsername, kTestingAppId, in_message4);
 
   consumer()->gcm_event_router()->clear_results();
@@ -1340,7 +1376,7 @@
   // Incoming message will go through for the new signed-in user.
   GCMClient::IncomingMessage in_message5;
   in_message5.data["in5"] = "in_data5";
-  GetGCMClientMock()->ReceiveMessage(
+  consumer()->GetGCMClient()->ReceiveMessage(
       kTestingUsername3, kTestingAppId, in_message5);
 
   consumer()->gcm_event_router()->clear_results();
diff --git a/google_apis/gcm/gcm_client.h b/google_apis/gcm/gcm_client.h
index 46ba561..c9203e8 100644
--- a/google_apis/gcm/gcm_client.h
+++ b/google_apis/gcm/gcm_client.h
@@ -12,6 +12,21 @@
 #include "base/basictypes.h"
 #include "google_apis/gcm/base/gcm_export.h"
 
+template <class T> class scoped_refptr;
+
+namespace base {
+class FilePath;
+class SequencedTaskRunner;
+}
+
+namespace checkin_proto {
+class ChromeBuildProto;
+}
+
+namespace net {
+class URLRequestContextGetter;
+}
+
 namespace gcm {
 
 // Interface that encapsulates the network communications with the Google Cloud
@@ -133,6 +148,14 @@
   GCMClient();
   virtual ~GCMClient();
 
+  // Begins initialization of the GCM Client.
+  virtual void Initialize(
+      const checkin_proto::ChromeBuildProto& chrome_build_proto,
+      const base::FilePath& store_path,
+      const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner,
+      const scoped_refptr<net::URLRequestContextGetter>&
+          url_request_context_getter) = 0;
+
   // Sets the delegate to interact with related to a specific user.
   // |username|: the username (email address) used to check in with the server.
   // |delegate|: the delegate whose methods will be called asynchronously in
diff --git a/google_apis/gcm/gcm_client_impl.cc b/google_apis/gcm/gcm_client_impl.cc
index 92402e17..bf78cf6 100644
--- a/google_apis/gcm/gcm_client_impl.cc
+++ b/google_apis/gcm/gcm_client_impl.cc
@@ -141,7 +141,7 @@
 void GCMClientImpl::Initialize(
     const checkin_proto::ChromeBuildProto& chrome_build_proto,
     const base::FilePath& path,
-    scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
+    const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner,
     const scoped_refptr<net::URLRequestContextGetter>&
         url_request_context_getter) {
   DCHECK_EQ(UNINITIALIZED, state_);
diff --git a/google_apis/gcm/gcm_client_impl.h b/google_apis/gcm/gcm_client_impl.h
index a6bac754f..e20d86b 100644
--- a/google_apis/gcm/gcm_client_impl.h
+++ b/google_apis/gcm/gcm_client_impl.h
@@ -23,8 +23,6 @@
 
 namespace base {
 class Clock;
-class FilePath;
-class SequencedTaskRunner;
 }  // namespace base
 
 namespace net {
@@ -48,15 +46,13 @@
   GCMClientImpl();
   virtual ~GCMClientImpl();
 
-  // Begins initialization of the GCM Client.
-  void Initialize(
-      const checkin_proto::ChromeBuildProto& chrome_build_proto,
-      const base::FilePath& path,
-      scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
-      const scoped_refptr<net::URLRequestContextGetter>&
-          url_request_context_getter);
-
   // Overridden from GCMClient:
+  virtual void Initialize(
+      const checkin_proto::ChromeBuildProto& chrome_build_proto,
+      const base::FilePath& store_path,
+      const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner,
+      const scoped_refptr<net::URLRequestContextGetter>&
+          url_request_context_getter) OVERRIDE;
   virtual void SetUserDelegate(const std::string& username,
                                Delegate* delegate) OVERRIDE;
   virtual void CheckIn(const std::string& username) OVERRIDE;