Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / services / gcm / gcm_profile_service.cc
index 77fabbc..b58f7e6 100644 (file)
@@ -5,17 +5,24 @@
 #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"
+#endif
 #include "chrome/browser/extensions/extension_service.h"
-#include "chrome/browser/extensions/extension_system.h"
 #include "chrome/browser/extensions/state_store.h"
 #include "chrome/browser/services/gcm/gcm_client_factory.h"
 #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"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/notification_details.h"
 #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:
@@ -49,10 +102,13 @@ class GCMProfileService::DelayedTaskController {
   // Adds a task that will be invoked once we're ready.
   void AddTask(const std::string& app_id, base::Closure task);
 
-  // Marks that GCMClient is ready.
-  void SetGCMClientReady();
+  // Sets GCM ready status. GCM is ready only when check-in is completed and
+  // the GCMClient is fully initialized. If it is set to ready for the first
+  // time, all the pending tasks for any ready apps will be run.
+  void SetGCMReady();
 
-  // Marks that the app is ready to have operations performed.
+  // Sets ready status for the app. If GCM is already ready, all the pending
+  // tasks for this app will be run.
   void SetAppReady(const std::string& app_id);
 
   // Returns true if it is ready to perform operations for an app.
@@ -66,8 +122,7 @@ class GCMProfileService::DelayedTaskController {
     AppTaskQueue();
     ~AppTaskQueue();
 
-    // The flag that indicates if GCMProfileService completes loading the
-    // persistent data for the app.
+    // The flag that indicates if GCMClient is ready.
     bool ready;
 
     // Tasks to be invoked upon ready.
@@ -76,10 +131,11 @@ class GCMProfileService::DelayedTaskController {
 
   void RunTasks(AppTaskQueue* task_queue);
 
-  // Flag that indicates that GCMClient is ready.
-  bool gcm_client_ready_;
+  // Flag that indicates that GCM is done.
+  bool gcm_ready_;
 
-  // Map from app_id to callbacks.
+  // Map from app_id to AppTaskQueue storing the tasks that will be invoked
+  // when both GCM and the app get ready.
   typedef std::map<std::string, AppTaskQueue*> DelayedTaskMap;
   DelayedTaskMap delayed_task_map_;
 };
@@ -92,7 +148,7 @@ GCMProfileService::DelayedTaskController::AppTaskQueue::~AppTaskQueue() {
 }
 
 GCMProfileService::DelayedTaskController::DelayedTaskController()
-    : gcm_client_ready_(false) {
+    : gcm_ready_(false) {
 }
 
 GCMProfileService::DelayedTaskController::~DelayedTaskController() {
@@ -110,7 +166,11 @@ void GCMProfileService::DelayedTaskController::AddApp(
 
 void GCMProfileService::DelayedTaskController::RemoveApp(
     const std::string& app_id) {
-  delayed_task_map_.erase(app_id);
+  DelayedTaskMap::iterator iter = delayed_task_map_.find(app_id);
+  if (iter == delayed_task_map_.end())
+    return;
+  delete iter->second;
+  delayed_task_map_.erase(iter);
 }
 
 void GCMProfileService::DelayedTaskController::AddTask(
@@ -120,19 +180,13 @@ void GCMProfileService::DelayedTaskController::AddTask(
   iter->second->tasks.push_back(task);
 }
 
-void GCMProfileService::DelayedTaskController::SetGCMClientReady() {
-  gcm_client_ready_ = true;
+void GCMProfileService::DelayedTaskController::SetGCMReady() {
+  gcm_ready_ = true;
 
   for (DelayedTaskMap::iterator iter = delayed_task_map_.begin();
-       iter != delayed_task_map_.end();) {
-    if (iter->second->ready) {
-      AppTaskQueue* task_queue = iter->second;
-      RunTasks(task_queue);
-      delete task_queue;
-      delayed_task_map_.erase(iter++);
-    } else {
-      ++iter;
-    }
+       iter != delayed_task_map_.end(); ++iter) {
+    if (iter->second->ready)
+      RunTasks(iter->second);
   }
 }
 
@@ -145,16 +199,13 @@ void GCMProfileService::DelayedTaskController::SetAppReady(
   DCHECK(task_queue);
   task_queue->ready = true;
 
-  if (gcm_client_ready_) {
+  if (gcm_ready_)
     RunTasks(task_queue);
-    delete task_queue;
-    delayed_task_map_.erase(iter);
-  }
 }
 
 bool GCMProfileService::DelayedTaskController::CanRunTaskWithoutDelay(
     const std::string& app_id) const {
-  if (!gcm_client_ready_)
+  if (!gcm_ready_)
     return false;
   DelayedTaskMap::const_iterator iter = delayed_task_map_.find(app_id);
   if (iter == delayed_task_map_.end())
@@ -169,7 +220,7 @@ bool GCMProfileService::DelayedTaskController::IsAppTracked(
 
 void GCMProfileService::DelayedTaskController::RunTasks(
     AppTaskQueue* task_queue) {
-  DCHECK(gcm_client_ready_ && task_queue->ready);
+  DCHECK(gcm_ready_ && task_queue->ready);
 
   for (size_t i = 0; i < task_queue->tasks.size(); ++i)
     task_queue->tasks[i].Run();
@@ -185,11 +236,11 @@ class GCMProfileService::IOWorker
 
   // Overridden from GCMClient::Delegate:
   // Called on IO thread.
-  virtual void OnCheckInFinished(const GCMClient::CheckinInfo& checkin_info,
-                                 GCMClient::Result result) OVERRIDE;
   virtual void OnRegisterFinished(const std::string& app_id,
                                   const std::string& registration_id,
                                   GCMClient::Result result) OVERRIDE;
+  virtual void OnUnregisterFinished(const std::string& app_id,
+                                    bool success) OVERRIDE;
   virtual void OnSendFinished(const std::string& app_id,
                               const std::string& message_id,
                               GCMClient::Result result) OVERRIDE;
@@ -200,15 +251,15 @@ class GCMProfileService::IOWorker
   virtual void OnMessageSendError(const std::string& app_id,
                                   const std::string& message_id,
                                   GCMClient::Result result) OVERRIDE;
-  virtual GCMClient::CheckinInfo GetCheckinInfo() const OVERRIDE;
-  virtual void OnLoadingCompleted() OVERRIDE;
+  virtual void OnGCMReady() OVERRIDE;
 
   // Called on IO thread.
-  void Initialize();
-  void SetUser(const std::string& username);
-  void RemoveUser();
-  void CheckIn();
-  void SetCheckinInfo(const GCMClient::CheckinInfo& checkin_info);
+  void Initialize(
+      GCMClientFactory* gcm_client_factory,
+      const base::FilePath& store_path,
+      const scoped_refptr<net::URLRequestContextGetter>&
+          url_request_context_getter);
+  void Reset();
   void CheckOut();
   void Register(const std::string& app_id,
                 const std::vector<std::string>& sender_ids,
@@ -224,54 +275,59 @@ class GCMProfileService::IOWorker
 
   const base::WeakPtr<GCMProfileService> service_;
 
-  // Not owned.
-  GCMClient* gcm_client_;
-
-  // The username (email address) of the signed-in user.
-  std::string username_;
-
-  // The checkin info obtained from the server for the signed in user associated
-  // with the profile.
-  GCMClient::CheckinInfo checkin_info_;
+  scoped_ptr<GCMClient> gcm_client_;
 };
 
 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(
+    GCMClientFactory* gcm_client_factory,
+    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_factory->BuildInstance().Pass();
+
+  checkin_proto::ChromeBuildProto chrome_build_proto;
+  chrome_build_proto.set_platform(GetPlatform());
+  chrome_build_proto.set_chrome_version(GetVersion());
+  chrome_build_proto.set_channel(GetChannel());
+
+  scoped_refptr<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,
+                          this);
 
   content::BrowserThread::PostTask(
       content::BrowserThread::UI,
       FROM_HERE,
-      base::Bind(&GCMProfileService::CheckGCMClientLoadingFinished,
+      base::Bind(&GCMProfileService::FinishInitializationOnUI,
                  service_,
-                 gcm_client_->IsLoading()));
+                 gcm_client_->IsReady()));
 }
 
-void GCMProfileService::IOWorker::OnCheckInFinished(
-    const GCMClient::CheckinInfo& checkin_info,
-    GCMClient::Result result) {
+void GCMProfileService::IOWorker::Reset() {
   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
 
-  checkin_info_ = checkin_info;
-
-  content::BrowserThread::PostTask(
-      content::BrowserThread::UI,
-      FROM_HERE,
-      base::Bind(&GCMProfileService::CheckInFinished,
-                 service_,
-                 checkin_info_,
-                 result));
+  // GCMClient instance must be destroyed from the same thread where it was
+  // created.
+  gcm_client_.reset();
 }
 
 void GCMProfileService::IOWorker::OnRegisterFinished(
@@ -290,6 +346,12 @@ void GCMProfileService::IOWorker::OnRegisterFinished(
                  result));
 }
 
+void GCMProfileService::IOWorker::OnUnregisterFinished(
+    const std::string& app_id,
+    bool success) {
+  // Nothing to do here.
+}
+
 void GCMProfileService::IOWorker::OnSendFinished(
     const std::string& app_id,
     const std::string& message_id,
@@ -347,53 +409,19 @@ void GCMProfileService::IOWorker::OnMessageSendError(
                  result));
 }
 
-GCMClient::CheckinInfo GCMProfileService::IOWorker::GetCheckinInfo() const {
-  return checkin_info_;
-}
-
-void GCMProfileService::IOWorker::OnLoadingCompleted() {
+void GCMProfileService::IOWorker::OnGCMReady() {
   content::BrowserThread::PostTask(
       content::BrowserThread::UI,
       FROM_HERE,
-      base::Bind(&GCMProfileService::GCMClientLoadingFinished,
+      base::Bind(&GCMProfileService::GCMClientReady,
                  service_));
 }
 
-void GCMProfileService::IOWorker::SetUser(const std::string& username) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
-  DCHECK(username_.empty() && !username.empty());
-
-  username_ = username;
-  gcm_client_->SetUserDelegate(username_, this);
-}
-
-void GCMProfileService::IOWorker::RemoveUser() {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
-
-  if (username_.empty())
-    return;
-  gcm_client_->SetUserDelegate(username_, NULL);
-  username_.clear();
-}
-
-void GCMProfileService::IOWorker::CheckIn() {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
-
-  gcm_client_->CheckIn(username_);
-}
-
-void GCMProfileService::IOWorker::SetCheckinInfo(
-    const GCMClient::CheckinInfo& checkin_info) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
-
-  checkin_info_ = checkin_info;
-}
-
 void GCMProfileService::IOWorker::CheckOut() {
   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
 
-  checkin_info_.Reset();
-  RemoveUser();
+  gcm_client_->CheckOut();
+  gcm_client_.reset();
 }
 
 void GCMProfileService::IOWorker::Register(
@@ -401,16 +429,14 @@ void GCMProfileService::IOWorker::Register(
     const std::vector<std::string>& sender_ids,
     const std::string& cert) {
   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
-  DCHECK(!username_.empty() && checkin_info_.IsValid());
 
-  gcm_client_->Register(username_, app_id, cert, sender_ids);
+  gcm_client_->Register(app_id, cert, sender_ids);
 }
 
 void GCMProfileService::IOWorker::Unregister(const std::string& app_id) {
   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
-  DCHECK(!username_.empty() && checkin_info_.IsValid());
 
-  gcm_client_->Unregister(username_, app_id);
+  gcm_client_->Unregister(app_id);
 }
 
 void GCMProfileService::IOWorker::Send(
@@ -418,9 +444,8 @@ void GCMProfileService::IOWorker::Send(
     const std::string& receiver_id,
     const GCMClient::OutgoingMessage& message) {
   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
-  DCHECK(!username_.empty() && checkin_info_.IsValid());
 
-  gcm_client_->Send(username_, app_id, receiver_id, message);
+  gcm_client_->Send(app_id, receiver_id, message);
 }
 
 GCMProfileService::RegistrationInfo::RegistrationInfo() {
@@ -444,61 +469,50 @@ bool GCMProfileService::IsGCMEnabled(Profile* profile) {
   if (enable_gcm_for_testing_)
     return true;
 
-  // GCM support is only enabled for Canary/Dev builds.
-  chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
-  return channel == chrome::VersionInfo::CHANNEL_UNKNOWN ||
-         channel == chrome::VersionInfo::CHANNEL_CANARY ||
-         channel == chrome::VersionInfo::CHANNEL_DEV;
+  return profile->GetPrefs()->GetBoolean(prefs::kGCMChannelEnabled);
 }
 
 // static
 void GCMProfileService::RegisterProfilePrefs(
     user_prefs::PrefRegistrySyncable* registry) {
-  registry->RegisterUint64Pref(
-      prefs::kGCMUserAccountID,
-      0,
+  // GCM support is only enabled by default for Canary/Dev/Custom builds.
+  chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
+  bool on_by_default = false;
+  if (channel == chrome::VersionInfo::CHANNEL_UNKNOWN ||
+      channel == chrome::VersionInfo::CHANNEL_CANARY ||
+      channel == chrome::VersionInfo::CHANNEL_DEV) {
+    on_by_default = true;
+  }
+  registry->RegisterBooleanPref(
+      prefs::kGCMChannelEnabled,
+      on_by_default,
       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
-  registry->RegisterStringPref(
-      prefs::kGCMUserToken,
-      "",
+  registry->RegisterListPref(
+      prefs::kGCMRegisteredAppIDs,
       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
 }
 
 GCMProfileService::GCMProfileService(Profile* profile)
     : profile_(profile),
+      gcm_client_ready_(false),
       testing_delegate_(NULL),
       weak_ptr_factory_(this) {
   DCHECK(!profile->IsOffTheRecord());
 }
 
 GCMProfileService::~GCMProfileService() {
-  if (username_.empty())
-    return;
-  content::BrowserThread::PostTask(
-      content::BrowserThread::IO,
-      FROM_HERE,
-      base::Bind(&GCMProfileService::IOWorker::RemoveUser,
-                 io_worker_));
 }
 
-void GCMProfileService::Initialize() {
-  delayed_task_controller_.reset(new DelayedTaskController);
+void GCMProfileService::Initialize(
+    scoped_ptr<GCMClientFactory> gcm_client_factory) {
+  gcm_client_factory_ = gcm_client_factory.Pass();
 
   // This has to be done first since CheckIn depends on it.
   io_worker_ = new IOWorker(weak_ptr_factory_.GetWeakPtr());
 
-  // This initializes GCMClient and also does the check to find out if GCMClient
-  // has finished the loading.
-  content::BrowserThread::PostTask(
-      content::BrowserThread::IO,
-      FROM_HERE,
-      base::Bind(&GCMProfileService::IOWorker::Initialize, io_worker_));
-
-  // In case that the profile has been signed in before GCMProfileService is
-  // created.
-  SigninManagerBase* manager = SigninManagerFactory::GetForProfile(profile_);
-  if (manager)
-    AddUser(manager->GetAuthenticatedUsername());
+#if !defined(OS_ANDROID)
+  js_event_router_.reset(new extensions::GcmJsEventRouter(profile_));
+#endif
 
   registrar_.Add(this,
                  chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL,
@@ -506,13 +520,22 @@ void GCMProfileService::Initialize() {
   registrar_.Add(this,
                  chrome::NOTIFICATION_GOOGLE_SIGNED_OUT,
                  content::Source<Profile>(profile_));
-  // TODO(jianli): move extension specific logic out of GCMProfileService.
   registrar_.Add(this,
-                 chrome::NOTIFICATION_EXTENSION_LOADED,
+                 chrome::NOTIFICATION_PROFILE_DESTROYED,
                  content::Source<Profile>(profile_));
+  // TODO(jianli): move extension specific logic out of GCMProfileService.
   registrar_.Add(this,
                  chrome:: NOTIFICATION_EXTENSION_UNINSTALLED,
                  content::Source<Profile>(profile_));
+
+  // In case that the profile has been signed in before GCMProfileService is
+  // created.
+  SigninManagerBase* manager = SigninManagerFactory::GetForProfile(profile_);
+  if (manager) {
+    std::string username = manager->GetAuthenticatedUsername();
+    if (!username.empty())
+      CheckIn(username);
+  }
 }
 
 void GCMProfileService::Register(const std::string& app_id,
@@ -534,7 +557,11 @@ void GCMProfileService::Register(const std::string& app_id,
     return;
   }
 
-  // Delay the register operation until the loading is done.
+  register_callbacks_[app_id] = callback;
+
+  EnsureAppReady(app_id);
+
+  // Delay the register operation until GCMClient is ready.
   if (!delayed_task_controller_->CanRunTaskWithoutDelay(app_id)) {
     delayed_task_controller_->AddTask(
         app_id,
@@ -542,18 +569,23 @@ void GCMProfileService::Register(const std::string& app_id,
                    weak_ptr_factory_.GetWeakPtr(),
                    app_id,
                    sender_ids,
-                   cert,
-                   callback));
+                   cert));
     return;
   }
 
-  DoRegister(app_id, sender_ids, cert, callback);
+  DoRegister(app_id, sender_ids, cert);
 }
 
 void GCMProfileService::DoRegister(const std::string& app_id,
                                    const std::vector<std::string>& sender_ids,
-                                   const std::string& cert,
-                                   RegisterCallback callback) {
+                                   const std::string& cert) {
+  std::map<std::string, RegisterCallback>::iterator callback_iter =
+      register_callbacks_.find(app_id);
+  if (callback_iter == register_callbacks_.end()) {
+    // The callback could have been removed when the app is uninstalled.
+    return;
+  }
+
   // Normalize the sender IDs by making them sorted.
   std::vector<std::string> normalized_sender_ids = sender_ids;
   std::sort(normalized_sender_ids.begin(), normalized_sender_ids.end());
@@ -564,6 +596,8 @@ void GCMProfileService::DoRegister(const std::string& app_id,
       registration_info_map_.find(app_id);
   if (registration_info_iter != registration_info_map_.end() &&
       registration_info_iter->second.sender_ids == normalized_sender_ids) {
+    RegisterCallback callback = callback_iter->second;
+    register_callbacks_.erase(callback_iter);
     callback.Run(registration_info_iter->second.registration_id,
                  GCMClient::SUCCESS);
     return;
@@ -575,7 +609,9 @@ void GCMProfileService::DoRegister(const std::string& app_id,
   registration_info.sender_ids = normalized_sender_ids;
   registration_info_map_[app_id] = registration_info;
 
-  register_callbacks_[app_id] = callback;
+  // Save the IDs of all registered apps such that we know what to remove from
+  // the the app's state store when the profile is signed out.
+  WriteRegisteredAppIDs();
 
   content::BrowserThread::PostTask(
       content::BrowserThread::IO,
@@ -608,7 +644,9 @@ void GCMProfileService::Send(const std::string& app_id,
   }
   send_callbacks_[key] = callback;
 
-  // Delay the send operation until all the loadings are done.
+  EnsureAppReady(app_id);
+
+  // Delay the send operation until all GCMClient is ready.
   if (!delayed_task_controller_->CanRunTaskWithoutDelay(app_id)) {
     delayed_task_controller_->AddTask(
         app_id,
@@ -645,22 +683,17 @@ void GCMProfileService::Observe(int type,
     case chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL: {
       const GoogleServiceSigninSuccessDetails* signin_details =
           content::Details<GoogleServiceSigninSuccessDetails>(details).ptr();
-      AddUser(signin_details->username);
+      // This could be called multiple times when the password changed.
+      if (username_ != signin_details->username)
+        CheckIn(signin_details->username);
       break;
     }
     case chrome::NOTIFICATION_GOOGLE_SIGNED_OUT:
-      username_.clear();
-      RemoveUser();
+      CheckOut();
       break;
-    case chrome::NOTIFICATION_EXTENSION_LOADED: {
-      extensions::Extension* extension =
-          content::Details<extensions::Extension>(details).ptr();
-      // No need to load the persisted registration info if the extension does
-      // not have the GCM permission.
-      if (extension->HasAPIPermission(extensions::APIPermission::kGcm))
-        ReadRegistrationInfo(extension->id());
+    case chrome::NOTIFICATION_PROFILE_DESTROYED:
+      ResetGCMClient();
       break;
-    }
     case chrome:: NOTIFICATION_EXTENSION_UNINSTALLED: {
       extensions::Extension* extension =
           content::Details<extensions::Extension>(details).ptr();
@@ -672,67 +705,70 @@ void GCMProfileService::Observe(int type,
   }
 }
 
-void GCMProfileService::AddUser(const std::string& username) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-
-  // If re-signin occurs due to password change, there is no need to do
-  // check-in again.
-  if (username_ == username || username.empty())
-    return;
+void GCMProfileService::CheckIn(const std::string& username) {
+  DCHECK(!username.empty() && username_.empty());
   username_ = username;
 
+  DCHECK(!delayed_task_controller_);
+  delayed_task_controller_.reset(new DelayedTaskController);
+
+  // Load all register apps.
+  ReadRegisteredAppIDs();
+
+  // Let the IO thread create and initialize GCMClient.
+  scoped_refptr<net::URLRequestContextGetter> url_request_context_getter =
+      profile_->GetRequestContext();
   content::BrowserThread::PostTask(
       content::BrowserThread::IO,
       FROM_HERE,
-      base::Bind(&GCMProfileService::IOWorker::SetUser,
+      base::Bind(&GCMProfileService::IOWorker::Initialize,
                  io_worker_,
-                 username_));
-
-  // Try to read persisted check-in info from the profile's prefs store.
-  PrefService* pref_service = profile_->GetPrefs();
-  uint64 android_id = pref_service->GetUint64(prefs::kGCMUserAccountID);
-  std::string base64_token = pref_service->GetString(prefs::kGCMUserToken);
-  std::string encrypted_secret;
-  base::Base64Decode(base::StringPiece(base64_token), &encrypted_secret);
-  if (android_id && !encrypted_secret.empty()) {
-    std::string decrypted_secret;
-    Encryptor::DecryptString(encrypted_secret, &decrypted_secret);
-    uint64 secret = 0;
-    if (base::StringToUint64(decrypted_secret, &secret) && secret) {
-      GCMClient::CheckinInfo checkin_info;
-      checkin_info.android_id = android_id;
-      checkin_info.secret = secret;
-      content::BrowserThread::PostTask(
-          content::BrowserThread::IO,
-          FROM_HERE,
-          base::Bind(&GCMProfileService::IOWorker::SetCheckinInfo,
-                     io_worker_,
-                     checkin_info));
-
-      if (testing_delegate_)
-        testing_delegate_->CheckInFinished(checkin_info, GCMClient::SUCCESS);
-
-      return;
-    }
+                 gcm_client_factory_.get(),
+                 profile_->GetPath().Append(chrome::kGCMStoreDirname),
+                 url_request_context_getter));
+}
+
+void GCMProfileService::CheckOut() {
+  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+  DCHECK(!username_.empty());
+  username_.clear();
+
+  // Remove persisted data from app's state store.
+  for (RegistrationInfoMap::const_iterator iter =
+           registration_info_map_.begin();
+       iter != registration_info_map_.end(); ++iter) {
+    DeleteRegistrationInfo(iter->first);
   }
 
+  // Remove persisted data from prefs store.
+  profile_->GetPrefs()->ClearPref(prefs::kGCMChannelEnabled);
+  profile_->GetPrefs()->ClearPref(prefs::kGCMRegisteredAppIDs);
+
+  gcm_client_ready_ = false;
+  delayed_task_controller_.reset();
+  register_callbacks_.clear();
+  send_callbacks_.clear();
+  registration_info_map_.clear();
+
   content::BrowserThread::PostTask(
       content::BrowserThread::IO,
       FROM_HERE,
-      base::Bind(&GCMProfileService::IOWorker::CheckIn, io_worker_));
+      base::Bind(&GCMProfileService::IOWorker::CheckOut, io_worker_));
 }
 
-void GCMProfileService::RemoveUser() {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-
-  PrefService* pref_service = profile_->GetPrefs();
-  pref_service->ClearPref(prefs::kGCMUserAccountID);
-  pref_service->ClearPref(prefs::kGCMUserToken);
-
+void GCMProfileService::ResetGCMClient() {
   content::BrowserThread::PostTask(
       content::BrowserThread::IO,
       FROM_HERE,
-      base::Bind(&GCMProfileService::IOWorker::CheckOut, io_worker_));
+      base::Bind(&GCMProfileService::IOWorker::Reset, io_worker_));
+}
+
+void GCMProfileService::EnsureAppReady(const std::string& app_id) {
+  if (delayed_task_controller_->IsAppTracked(app_id))
+    return;
+
+  ReadRegistrationInfo(app_id);
 }
 
 void GCMProfileService::Unregister(const std::string& app_id) {
@@ -750,6 +786,9 @@ void GCMProfileService::Unregister(const std::string& app_id) {
     return;
   registration_info_map_.erase(registration_info_iter);
 
+  // Update the persisted IDs of registered apps.
+  WriteRegisteredAppIDs();
+
   // Remove the persisted registration info.
   DeleteRegistrationInfo(app_id);
 
@@ -767,29 +806,6 @@ void GCMProfileService::Unregister(const std::string& app_id) {
                  app_id));
 }
 
-void GCMProfileService::CheckInFinished(
-    const GCMClient::CheckinInfo& checkin_info, GCMClient::Result result) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-
-  // Save the check-in info into the profile's prefs store.
-  PrefService* pref_service = profile_->GetPrefs();
-  pref_service->SetUint64(prefs::kGCMUserAccountID, checkin_info.android_id);
-
-  // Encrypt the secret for persisting purpose.
-  std::string encrypted_secret;
-  Encryptor::EncryptString(base::Uint64ToString(checkin_info.secret),
-                           &encrypted_secret);
-
-  // |encrypted_secret| might contain binary data and our prefs store only
-  // works for the text.
-  std::string base64_token;
-  base::Base64Encode(encrypted_secret, &base64_token);
-  pref_service->SetString(prefs::kGCMUserToken, base64_token);
-
-  if (testing_delegate_)
-    testing_delegate_->CheckInFinished(checkin_info, result);
-}
-
 void GCMProfileService::RegisterFinished(const std::string& app_id,
                                          const std::string& registration_id,
                                          GCMClient::Result result) {
@@ -872,24 +888,54 @@ void GCMProfileService::MessageSendError(const std::string& app_id,
   GetEventRouter(app_id)->OnSendError(app_id, message_id, result);
 }
 
-void GCMProfileService::CheckGCMClientLoadingFinished(bool is_loading) {
+void GCMProfileService::FinishInitializationOnUI(bool ready) {
   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
 
-  if (!is_loading)
-    delayed_task_controller_->SetGCMClientReady();
+  gcm_client_ready_ = ready;
+  if (gcm_client_ready_)
+    delayed_task_controller_->SetGCMReady();
 }
 
-void GCMProfileService::GCMClientLoadingFinished() {
+void GCMProfileService::GCMClientReady() {
   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
 
-  delayed_task_controller_->SetGCMClientReady();
+  if (gcm_client_ready_)
+    return;
+  gcm_client_ready_ = true;
+
+  delayed_task_controller_->SetGCMReady();
 }
 
-GCMEventRouter* GCMProfileService::GetEventRouter(const std::string& app_id) {
+GCMEventRouter* GCMProfileService::GetEventRouter(const std::string& app_id)
+    const {
   if (testing_delegate_ && testing_delegate_->GetEventRouter())
     return testing_delegate_->GetEventRouter();
-  // TODO(fgorski): check and create the event router for JS routing.
+#if defined(OS_ANDROID)
+  return NULL;
+#else
   return js_event_router_.get();
+#endif
+}
+
+void GCMProfileService::ReadRegisteredAppIDs() {
+  const base::ListValue* app_id_list =
+      profile_->GetPrefs()->GetList(prefs::kGCMRegisteredAppIDs);
+  for (size_t i = 0; i < app_id_list->GetSize(); ++i) {
+    std::string app_id;
+    if (!app_id_list->GetString(i, &app_id))
+      continue;
+    ReadRegistrationInfo(app_id);
+  }
+}
+
+void GCMProfileService::WriteRegisteredAppIDs() {
+  base::ListValue apps;
+  for (RegistrationInfoMap::const_iterator iter =
+           registration_info_map_.begin();
+       iter != registration_info_map_.end(); ++iter) {
+    apps.Append(new base::StringValue(iter->first));
+  }
+  profile_->GetPrefs()->Set(prefs::kGCMRegisteredAppIDs, apps);
 }
 
 void GCMProfileService::DeleteRegistrationInfo(const std::string& app_id) {
@@ -930,11 +976,6 @@ void GCMProfileService::WriteRegistrationInfo(const std::string& app_id) {
 }
 
 void GCMProfileService::ReadRegistrationInfo(const std::string& app_id) {
-  // This function can be called more than once when the app is allowed in
-  // incognito and the extension service reloads the app.
-  if (delayed_task_controller_->IsAppTracked(app_id))
-    return;
-
   delayed_task_controller_->AddApp(app_id);
 
   extensions::StateStore* storage =