Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / sync / profile_sync_service.cc
index 9166d4f..29deb31 100644 (file)
@@ -6,53 +6,53 @@
 
 #include <cstddef>
 #include <map>
-#include <set>
-#include <utility>
+#include <vector>
 
 #include "base/basictypes.h"
 #include "base/bind.h"
+#include "base/bind_helpers.h"
 #include "base/callback.h"
 #include "base/command_line.h"
 #include "base/compiler_specific.h"
+#include "base/files/file_util.h"
 #include "base/logging.h"
 #include "base/memory/ref_counted.h"
 #include "base/message_loop/message_loop.h"
 #include "base/metrics/histogram.h"
+#include "base/profiler/scoped_tracker.h"
 #include "base/strings/string16.h"
 #include "base/strings/stringprintf.h"
 #include "base/threading/thread_restrictions.h"
 #include "build/build_config.h"
 #include "chrome/browser/bookmarks/enhanced_bookmarks_features.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/browsing_data/browsing_data_helper.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/defaults.h"
+#include "chrome/browser/invalidation/profile_invalidation_provider_factory.h"
 #include "chrome/browser/net/chrome_cookie_notification_details.h"
+#include "chrome/browser/password_manager/password_store_factory.h"
+#include "chrome/browser/prefs/chrome_pref_service_factory.h"
 #include "chrome/browser/prefs/pref_service_syncable.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/services/gcm/gcm_profile_service.h"
 #include "chrome/browser/services/gcm/gcm_profile_service_factory.h"
-#include "chrome/browser/signin/about_signin_internals.h"
 #include "chrome/browser/signin/about_signin_internals_factory.h"
+#include "chrome/browser/signin/chrome_signin_client_factory.h"
 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
-#include "chrome/browser/signin/signin_manager.h"
 #include "chrome/browser/signin/signin_manager_factory.h"
 #include "chrome/browser/sync/backend_migrator.h"
-#include "chrome/browser/sync/glue/change_processor.h"
 #include "chrome/browser/sync/glue/chrome_report_unrecoverable_error.h"
-#include "chrome/browser/sync/glue/device_info.h"
 #include "chrome/browser/sync/glue/favicon_cache.h"
-#include "chrome/browser/sync/glue/session_data_type_controller.h"
-#include "chrome/browser/sync/glue/session_model_associator.h"
 #include "chrome/browser/sync/glue/sync_backend_host.h"
 #include "chrome/browser/sync/glue/sync_backend_host_impl.h"
 #include "chrome/browser/sync/glue/sync_start_util.h"
-#include "chrome/browser/sync/glue/synced_device_tracker.h"
 #include "chrome/browser/sync/glue/typed_url_data_type_controller.h"
-#include "chrome/browser/sync/managed_user_signin_manager_wrapper.h"
 #include "chrome/browser/sync/profile_sync_components_factory_impl.h"
-#include "chrome/browser/sync/sessions2/notification_service_sessions_router.h"
-#include "chrome/browser/sync/sessions2/sessions_sync_manager.h"
+#include "chrome/browser/sync/sessions/notification_service_sessions_router.h"
+#include "chrome/browser/sync/supervised_user_signin_manager_wrapper.h"
 #include "chrome/browser/sync/sync_error_controller.h"
+#include "chrome/browser/sync/sync_type_preference_provider.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/common/chrome_version_info.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/gcm_driver/gcm_driver.h"
+#include "components/invalidation/invalidation_service.h"
+#include "components/invalidation/profile_invalidation_provider.h"
+#include "components/password_manager/core/browser/password_store.h"
+#include "components/pref_registry/pref_registry_syncable.h"
+#include "components/signin/core/browser/about_signin_internals.h"
 #include "components/signin/core/browser/profile_oauth2_token_service.h"
+#include "components/signin/core/browser/signin_manager.h"
+#include "components/signin/core/browser/signin_metrics.h"
+#include "components/sync_driver/change_processor.h"
 #include "components/sync_driver/data_type_controller.h"
+#include "components/sync_driver/device_info.h"
 #include "components/sync_driver/pref_names.h"
 #include "components/sync_driver/system_encryptor.h"
 #include "components/sync_driver/user_selectable_sync_type.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_service.h"
 #include "content/public/browser/notification_source.h"
-#include "google_apis/gaia/gaia_constants.h"
-#include "grit/generated_resources.h"
 #include "net/cookies/cookie_monster.h"
 #include "net/url_request/url_request_context_getter.h"
 #include "sync/api/sync_error.h"
 #include "sync/internal_api/public/configure_reason.h"
 #include "sync/internal_api/public/http_bridge_network_resources.h"
 #include "sync/internal_api/public/network_resources.h"
+#include "sync/internal_api/public/sessions/type_debug_info_observer.h"
+#include "sync/internal_api/public/shutdown_reason.h"
+#include "sync/internal_api/public/sync_context_proxy.h"
 #include "sync/internal_api/public/sync_encryption_handler.h"
 #include "sync/internal_api/public/util/experiments.h"
+#include "sync/internal_api/public/util/sync_db_util.h"
 #include "sync/internal_api/public/util/sync_string_conversions.h"
-#include "sync/js/js_arg_list.h"
 #include "sync/js/js_event_details.h"
 #include "sync/util/cryptographer.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/l10n/time_format.h"
 
-#if defined(ENABLE_MANAGED_USERS)
-#include "chrome/browser/managed_mode/managed_user_constants.h"
-#endif
-
 #if defined(OS_ANDROID)
 #include "sync/internal_api/public/read_transaction.h"
 #endif
 
-using browser_sync::ChangeProcessor;
-using browser_sync::DataTypeController;
-using browser_sync::DataTypeManager;
-using browser_sync::FailedDataTypesHandler;
 using browser_sync::NotificationServiceSessionsRouter;
 using browser_sync::ProfileSyncServiceStartBehavior;
+using browser_sync::SessionsSyncManager;
 using browser_sync::SyncBackendHost;
+using sync_driver::ChangeProcessor;
+using sync_driver::DataTypeController;
+using sync_driver::DataTypeManager;
+using sync_driver::DataTypeStatusTable;
+using sync_driver::DeviceInfoSyncService;
 using syncer::ModelType;
 using syncer::ModelTypeSet;
 using syncer::JsBackend;
@@ -153,6 +163,42 @@ const net::BackoffEntry::Policy kRequestAccessTokenBackoffPolicy = {
   false,
 };
 
+static const base::FilePath::CharType kSyncDataFolderName[] =
+    FILE_PATH_LITERAL("Sync Data");
+
+static const base::FilePath::CharType kSyncBackupDataFolderName[] =
+    FILE_PATH_LITERAL("Sync Data Backup");
+
+namespace {
+
+void ClearBrowsingData(BrowsingDataRemover::Observer* observer,
+                       Profile* profile,
+                       base::Time start,
+                       base::Time end) {
+  // BrowsingDataRemover deletes itself when it's done.
+  BrowsingDataRemover* remover = BrowsingDataRemover::CreateForRange(
+      profile, start, end);
+  if (observer)
+    remover->AddObserver(observer);
+  remover->Remove(BrowsingDataRemover::REMOVE_ALL,
+                  BrowsingDataHelper::ALL);
+
+  scoped_refptr<password_manager::PasswordStore> password =
+      PasswordStoreFactory::GetForProfile(profile, Profile::EXPLICIT_ACCESS);
+  password->RemoveLoginsSyncedBetween(start, end);
+}
+
+// Perform the actual sync data folder deletion.
+// This should only be called on the sync thread.
+void DeleteSyncDataFolder(const base::FilePath& directory_path) {
+  if (base::DirectoryExists(directory_path)) {
+    if (!base::DeleteFile(directory_path, true))
+      LOG(DFATAL) << "Could not delete the Sync Data folder.";
+  }
+}
+
+}  // anonymous namespace
+
 bool ShouldShowActionOnUI(
     const syncer::SyncProtocolError& error) {
   return (error.action != syncer::UNKNOWN_ACTION &&
@@ -161,26 +207,27 @@ bool ShouldShowActionOnUI(
 }
 
 ProfileSyncService::ProfileSyncService(
-    ProfileSyncComponentsFactory* factory,
+    scoped_ptr<ProfileSyncComponentsFactory> factory,
     Profile* profile,
-    ManagedUserSigninManagerWrapper* signin_wrapper,
+    scoped_ptr<SupervisedUserSigninManagerWrapper> signin_wrapper,
     ProfileOAuth2TokenService* oauth2_token_service,
     ProfileSyncServiceStartBehavior start_behavior)
     : OAuth2TokenService::Consumer("sync"),
       last_auth_error_(AuthError::AuthErrorNone()),
       passphrase_required_reason_(syncer::REASON_PASSPHRASE_NOT_REQUIRED),
-      factory_(factory),
+      factory_(factory.Pass()),
       profile_(profile),
       sync_prefs_(profile_->GetPrefs()),
-      sync_service_url_(kDevServerUrl),
+      sync_service_url_(GetSyncServiceURL(*CommandLine::ForCurrentProcess())),
       is_first_time_sync_configure_(false),
       backend_initialized_(false),
       sync_disabled_by_admin_(false),
       is_auth_in_progress_(false),
-      signin_(signin_wrapper),
+      signin_(signin_wrapper.Pass()),
       unrecoverable_error_reason_(ERROR_REASON_UNSET),
       expect_sync_configuration_aborted_(false),
       encrypted_types_(syncer::SyncEncryptionHandler::SensitiveTypes()),
+      encrypt_everything_allowed_(true),
       encrypt_everything_(false),
       encryption_pending_(false),
       configure_status_(DataTypeManager::UNKNOWN),
@@ -195,32 +242,36 @@ ProfileSyncService::ProfileSyncService(
           start_behavior,
           oauth2_token_service,
           &sync_prefs_,
-          signin_wrapper,
+          signin_.get(),
+          base::Bind(&ProfileSyncService::StartUpSlowBackendComponents,
+                     startup_controller_weak_factory_.GetWeakPtr(),
+                     SYNC)),
+      backup_rollback_controller_(
+          &sync_prefs_,
+          signin_.get(),
+          base::Bind(&ProfileSyncService::StartUpSlowBackendComponents,
+                     startup_controller_weak_factory_.GetWeakPtr(),
+                     BACKUP),
           base::Bind(&ProfileSyncService::StartUpSlowBackendComponents,
-                     startup_controller_weak_factory_.GetWeakPtr())) {
+                     startup_controller_weak_factory_.GetWeakPtr(),
+                     ROLLBACK)),
+      backend_mode_(IDLE),
+      need_backup_(false),
+      backup_finished_(false),
+      clear_browsing_data_(base::Bind(&ClearBrowsingData)),
+      browsing_data_remover_observer_(NULL) {
   DCHECK(profile);
-  // By default, dev, canary, and unbranded Chromium users will go to the
-  // development servers. Development servers have more features than standard
-  // sync servers. Users with officially-branded Chrome stable and beta builds
-  // will go to the standard sync servers.
-  //
-  // GetChannel hits the registry on Windows. See http://crbug.com/70380.
-  base::ThreadRestrictions::ScopedAllowIO allow_io;
-  chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
-  if (channel == chrome::VersionInfo::CHANNEL_STABLE ||
-      channel == chrome::VersionInfo::CHANNEL_BETA) {
-    sync_service_url_ = GURL(kSyncServerUrl);
-  }
+  syncer::SyncableService::StartSyncFlare flare(
+      sync_start_util::GetFlareForSyncableService(profile->GetPath()));
+  scoped_ptr<browser_sync::LocalSessionEventRouter> router(
+      new NotificationServiceSessionsRouter(profile, flare));
 
-  if (!CommandLine::ForCurrentProcess()->HasSwitch(
-      switches::kDisableSyncSessionsV2)) {
-    syncer::SyncableService::StartSyncFlare flare(
-        sync_start_util::GetFlareForSyncableService(profile->GetPath()));
-    scoped_ptr<browser_sync::LocalSessionEventRouter> router(
-        new NotificationServiceSessionsRouter(profile, flare));
-    sessions_sync_manager_.reset(
-        new SessionsSyncManager(profile, this, router.Pass()));
-  }
+  DCHECK(factory_.get());
+  local_device_ = factory_->CreateLocalDeviceInfoProvider();
+  sessions_sync_manager_.reset(
+      new SessionsSyncManager(profile, local_device_.get(), router.Pass()));
+  device_info_sync_service_.reset(
+      new DeviceInfoSyncService(local_device_.get()));
 }
 
 ProfileSyncService::~ProfileSyncService() {
@@ -247,8 +298,6 @@ bool ProfileSyncService::IsOAuthRefreshTokenAvailable() {
 }
 
 void ProfileSyncService::Initialize() {
-  InitSettings();
-
   // We clear this here (vs Shutdown) because we want to remember that an error
   // happened on shutdown so we can display details (message, location) about it
   // in about:sync.
@@ -271,8 +320,6 @@ void ProfileSyncService::Initialize() {
 
   TrySyncDatatypePrefRecovery();
 
-  last_synced_time_ = sync_prefs_.GetLastSyncedTime();
-
 #if defined(OS_CHROMEOS)
   std::string bootstrap_token = sync_prefs_.GetEncryptionBootstrapToken();
   if (bootstrap_token.empty()) {
@@ -288,12 +335,33 @@ void ProfileSyncService::Initialize() {
   AddObserver(sync_error_controller_.get());
 #endif
 
+  bool running_rollback = false;
+  if (browser_sync::BackupRollbackController::IsBackupEnabled()) {
+    // Backup is needed if user's not signed in or signed in but previous
+    // backup didn't finish, i.e. backend didn't switch from backup to sync.
+    need_backup_ = signin_->GetEffectiveUsername().empty() ||
+        sync_prefs_.GetFirstSyncTime().is_null();
+
+    // Try to resume rollback if it didn't finish in last session.
+    running_rollback = backup_rollback_controller_.StartRollback();
+  } else {
+    need_backup_ = false;
+  }
+
+#if defined(ENABLE_PRE_SYNC_BACKUP)
+  if (!running_rollback && signin_->GetEffectiveUsername().empty()) {
+    CleanUpBackup();
+  }
+#else
+  DCHECK(!running_rollback);
+#endif
+
   startup_controller_.Reset(GetRegisteredDataTypes());
   startup_controller_.TryStart();
 }
 
 void ProfileSyncService::TrySyncDatatypePrefRecovery() {
-  DCHECK(!sync_initialized());
+  DCHECK(!backend_initialized());
   if (!HasSyncSetupCompleted())
     return;
 
@@ -321,8 +389,6 @@ void ProfileSyncService::TrySyncDatatypePrefRecovery() {
   UMA_HISTOGRAM_COUNTS("Sync.DatatypePrefRecovery", 1);
   sync_prefs_.SetKeepEverythingSynced(true);
   syncer::ModelTypeSet registered_types = GetRegisteredDataTypes();
-  sync_prefs_.SetPreferredDataTypes(registered_types,
-                                    registered_types);
 }
 
 void ProfileSyncService::StartSyncingWithServer() {
@@ -344,167 +410,111 @@ void ProfileSyncService::UnregisterAuthNotifications() {
 
 void ProfileSyncService::RegisterDataTypeController(
     DataTypeController* data_type_controller) {
-  DCHECK_EQ(data_type_controllers_.count(data_type_controller->type()), 0U);
-  data_type_controllers_[data_type_controller->type()] =
+  DCHECK_EQ(
+      directory_data_type_controllers_.count(data_type_controller->type()),
+      0U);
+  DCHECK(!GetRegisteredNonBlockingDataTypes().Has(
+      data_type_controller->type()));
+  directory_data_type_controllers_[data_type_controller->type()] =
       data_type_controller;
 }
 
-browser_sync::SessionModelAssociator*
-    ProfileSyncService::GetSessionModelAssociatorDeprecated() {
-  if (!IsSessionsDataTypeControllerRunning())
-    return NULL;
-
-  // If we're using sessions V2, there's no model associator.
-  if (sessions_sync_manager_.get())
-    return NULL;
+void ProfileSyncService::RegisterNonBlockingType(syncer::ModelType type) {
+  DCHECK_EQ(directory_data_type_controllers_.count(type), 0U)
+      << "Duplicate registration of type " << ModelTypeToString(type);
 
-  return static_cast<browser_sync::SessionDataTypeController*>(
-      data_type_controllers_.find(
-      syncer::SESSIONS)->second.get())->GetModelAssociator();
+  // TODO(rlarocque): Set the enable flag properly when crbug.com/368834 is
+  // fixed and we have some way of telling whether or not this type should be
+  // enabled.
+  non_blocking_data_type_manager_.RegisterType(type, false);
 }
 
-bool ProfileSyncService::IsSessionsDataTypeControllerRunning() const {
-  return data_type_controllers_.find(syncer::SESSIONS) !=
-      data_type_controllers_.end() &&
-      data_type_controllers_.find(syncer::SESSIONS)->second->state() ==
-      DataTypeController::RUNNING;
+void ProfileSyncService::InitializeNonBlockingType(
+    syncer::ModelType type,
+    const scoped_refptr<base::SequencedTaskRunner>& task_runner,
+    const base::WeakPtr<syncer::ModelTypeSyncProxyImpl>& type_sync_proxy) {
+  non_blocking_data_type_manager_.InitializeType(
+      type, task_runner, type_sync_proxy);
 }
 
-browser_sync::OpenTabsUIDelegate* ProfileSyncService::GetOpenTabsUIDelegate() {
-  if (!IsSessionsDataTypeControllerRunning())
-    return NULL;
-
-  if (!CommandLine::ForCurrentProcess()->HasSwitch(
-      switches::kDisableSyncSessionsV2)) {
-    return sessions_sync_manager_.get();
-  } else {
-    return GetSessionModelAssociatorDeprecated();
+bool ProfileSyncService::IsDataTypeControllerRunning(
+    syncer::ModelType type) const {
+  DataTypeController::TypeMap::const_iterator iter =
+      directory_data_type_controllers_.find(type);
+  if (iter == directory_data_type_controllers_.end()) {
+    return false;
   }
+  return iter->second->state() == DataTypeController::RUNNING;
 }
 
-browser_sync::FaviconCache* ProfileSyncService::GetFaviconCache() {
-  // TODO(tim): Clean this up (or remove) once there's only one implementation.
-  // Bug 98892.
-  if (!CommandLine::ForCurrentProcess()->HasSwitch(
-      switches::kDisableSyncSessionsV2)) {
-    return sessions_sync_manager_->GetFaviconCache();
-  } else if (GetSessionModelAssociatorDeprecated()) {
-    return GetSessionModelAssociatorDeprecated()->GetFaviconCache();
-  } else {
+browser_sync::OpenTabsUIDelegate* ProfileSyncService::GetOpenTabsUIDelegate() {
+  if (!IsDataTypeControllerRunning(syncer::SESSIONS))
     return NULL;
-  }
-}
-
-scoped_ptr<browser_sync::DeviceInfo>
-ProfileSyncService::GetLocalDeviceInfo() const {
-  if (backend_) {
-    browser_sync::SyncedDeviceTracker* device_tracker =
-        backend_->GetSyncedDeviceTracker();
-    if (device_tracker)
-      return device_tracker->ReadLocalDeviceInfo();
-  }
-  return scoped_ptr<browser_sync::DeviceInfo>();
+  return sessions_sync_manager_.get();
 }
 
-scoped_ptr<browser_sync::DeviceInfo>
-ProfileSyncService::GetDeviceInfo(const std::string& client_id) const {
-  if (backend_) {
-    browser_sync::SyncedDeviceTracker* device_tracker =
-        backend_->GetSyncedDeviceTracker();
-    if (device_tracker)
-      return device_tracker->ReadDeviceInfo(client_id);
-  }
-  return scoped_ptr<browser_sync::DeviceInfo>();
+browser_sync::FaviconCache* ProfileSyncService::GetFaviconCache() {
+  return sessions_sync_manager_->GetFaviconCache();
 }
 
-ScopedVector<browser_sync::DeviceInfo>
-    ProfileSyncService::GetAllSignedInDevices() const {
-  ScopedVector<browser_sync::DeviceInfo> devices;
-  if (backend_) {
-    browser_sync::SyncedDeviceTracker* device_tracker =
-        backend_->GetSyncedDeviceTracker();
-    if (device_tracker) {
-      // TODO(lipalani) - Make device tracker return a scoped vector.
-      device_tracker->GetAllSyncedDeviceInfo(&devices);
-    }
-  }
-  return devices.Pass();
+browser_sync::SyncedWindowDelegatesGetter*
+ProfileSyncService::GetSyncedWindowDelegatesGetter() const {
+  return sessions_sync_manager_->GetSyncedWindowDelegatesGetter();
 }
 
-std::string ProfileSyncService::GetLocalSyncCacheGUID() const {
-  if (backend_) {
-    browser_sync::SyncedDeviceTracker* device_tracker =
-        backend_->GetSyncedDeviceTracker();
-    if (device_tracker) {
-      return device_tracker->cache_guid();
-    }
-  }
-  return std::string();
-}
+sync_driver::DeviceInfoTracker* ProfileSyncService::GetDeviceInfoTracker()
+    const {
+  if (!IsDataTypeControllerRunning(syncer::DEVICE_INFO))
+    return NULL;
 
-// Notifies the observer of any device info changes.
-void ProfileSyncService::AddObserverForDeviceInfoChange(
-    browser_sync::SyncedDeviceTracker::Observer* observer) {
-  if (backend_) {
-    browser_sync::SyncedDeviceTracker* device_tracker =
-        backend_->GetSyncedDeviceTracker();
-    if (device_tracker) {
-      device_tracker->AddObserver(observer);
-    }
-  }
+  return device_info_sync_service_.get();
 }
 
-// Removes the observer from device info change notification.
-void ProfileSyncService::RemoveObserverForDeviceInfoChange(
-    browser_sync::SyncedDeviceTracker::Observer* observer) {
-  if (backend_) {
-    browser_sync::SyncedDeviceTracker* device_tracker =
-        backend_->GetSyncedDeviceTracker();
-    if (device_tracker) {
-      device_tracker->RemoveObserver(observer);
-    }
-  }
+sync_driver::LocalDeviceInfoProvider*
+ProfileSyncService::GetLocalDeviceInfoProvider() {
+  return local_device_.get();
 }
 
 void ProfileSyncService::GetDataTypeControllerStates(
-  browser_sync::DataTypeController::StateMap* state_map) const {
-    for (browser_sync::DataTypeController::TypeMap::const_iterator iter =
-         data_type_controllers_.begin(); iter != data_type_controllers_.end();
+  DataTypeController::StateMap* state_map) const {
+    for (DataTypeController::TypeMap::const_iterator iter =
+         directory_data_type_controllers_.begin();
+         iter != directory_data_type_controllers_.end();
          ++iter)
       (*state_map)[iter->first] = iter->second.get()->state();
 }
 
-void ProfileSyncService::InitSettings() {
-  const CommandLine& command_line = *CommandLine::ForCurrentProcess();
-
-  // Override the sync server URL from the command-line, if sync server
-  // command-line argument exists.
-  if (command_line.HasSwitch(switches::kSyncServiceURL)) {
-    std::string value(command_line.GetSwitchValueASCII(
-        switches::kSyncServiceURL));
-    if (!value.empty()) {
-      GURL custom_sync_url(value);
-      if (custom_sync_url.is_valid()) {
-        sync_service_url_ = custom_sync_url;
-      } else {
-        LOG(WARNING) << "The following sync URL specified at the command-line "
-                     << "is invalid: " << value;
-      }
-    }
-  }
-}
-
 SyncCredentials ProfileSyncService::GetCredentials() {
   SyncCredentials credentials;
-  credentials.email = signin_->GetEffectiveUsername();
-  DCHECK(!credentials.email.empty());
-  credentials.sync_token = access_token_;
+  if (backend_mode_ == SYNC) {
+    credentials.email = signin_->GetEffectiveUsername();
+    DCHECK(!credentials.email.empty());
+    credentials.sync_token = access_token_;
+
+    if (credentials.sync_token.empty())
+      credentials.sync_token = "credentials_lost";
+
+    credentials.scope_set.insert(signin_->GetSyncScopeToUse());
+  }
 
-  if (credentials.sync_token.empty())
-    credentials.sync_token = "credentials_lost";
   return credentials;
 }
 
+bool ProfileSyncService::ShouldDeleteSyncFolder() {
+  switch (backend_mode_) {
+    case SYNC:
+      return !HasSyncSetupCompleted();
+    case BACKUP:
+      return true;
+    case ROLLBACK:
+      return false;
+    case IDLE:
+      NOTREACHED();
+      return true;
+  }
+  return true;
+}
+
 void ProfileSyncService::InitializeBackend(bool delete_stale_data) {
   if (!backend_) {
     NOTREACHED();
@@ -516,7 +526,7 @@ void ProfileSyncService::InitializeBackend(bool delete_stale_data) {
   scoped_refptr<net::URLRequestContextGetter> request_context_getter(
       profile_->GetRequestContext());
 
-  if (delete_stale_data)
+  if (backend_mode_ == SYNC && delete_stale_data)
     ClearStaleErrors();
 
   scoped_ptr<syncer::UnrecoverableErrorHandler>
@@ -532,7 +542,7 @@ void ProfileSyncService::InitializeBackend(bool delete_stale_data) {
       credentials,
       delete_stale_data,
       scoped_ptr<syncer::SyncManagerFactory>(
-          new syncer::SyncManagerFactory).Pass(),
+          new syncer::SyncManagerFactory(GetManagerType())).Pass(),
       backend_unrecoverable_error_handler.Pass(),
       &browser_sync::ChromeReportUnrecoverableError,
       network_resources_.get());
@@ -547,15 +557,6 @@ bool ProfileSyncService::IsEncryptedDatatypeEnabled() const {
   return !Intersection(preferred_types, encrypted_types).Empty();
 }
 
-void ProfileSyncService::OnSyncConfigureRetry() {
-  // Note: in order to handle auth failures that arise before the backend is
-  // initialized (e.g. from invalidation notifier, or downloading new control
-  // types), we have to gracefully handle configuration retries at all times.
-  // At this point an auth error badge should be shown, which once resolved
-  // will trigger a new sync cycle.
-  NotifyObservers();
-}
-
 void ProfileSyncService::OnProtocolEvent(
     const syncer::ProtocolEvent& event) {
   FOR_EACH_OBSERVER(browser_sync::ProtocolEventObserver,
@@ -563,6 +564,30 @@ void ProfileSyncService::OnProtocolEvent(
                     OnProtocolEvent(event));
 }
 
+void ProfileSyncService::OnDirectoryTypeCommitCounterUpdated(
+    syncer::ModelType type,
+    const syncer::CommitCounters& counters) {
+  FOR_EACH_OBSERVER(syncer::TypeDebugInfoObserver,
+                    type_debug_info_observers_,
+                    OnCommitCountersUpdated(type, counters));
+}
+
+void ProfileSyncService::OnDirectoryTypeUpdateCounterUpdated(
+    syncer::ModelType type,
+    const syncer::UpdateCounters& counters) {
+  FOR_EACH_OBSERVER(syncer::TypeDebugInfoObserver,
+                    type_debug_info_observers_,
+                    OnUpdateCountersUpdated(type, counters));
+}
+
+void ProfileSyncService::OnDirectoryTypeStatusCounterUpdated(
+    syncer::ModelType type,
+    const syncer::StatusCounters& counters) {
+  FOR_EACH_OBSERVER(syncer::TypeDebugInfoObserver,
+                    type_debug_info_observers_,
+                    OnStatusCountersUpdated(type, counters));
+}
+
 void ProfileSyncService::OnDataTypeRequestsSyncStartup(
     syncer::ModelType type) {
   DCHECK(syncer::UserTypes().Has(type));
@@ -583,23 +608,94 @@ void ProfileSyncService::OnDataTypeRequestsSyncStartup(
   startup_controller_.OnDataTypeRequestsSyncStartup(type);
 }
 
-void ProfileSyncService::StartUpSlowBackendComponents() {
-  // Don't start up multiple times.
-  DCHECK(!backend_);
+void ProfileSyncService::StartUpSlowBackendComponents(
+    ProfileSyncService::BackendMode mode) {
+  DCHECK_NE(IDLE, mode);
+  if (backend_mode_ == mode) {
+    return;
+  }
 
-  DCHECK(IsSyncEnabledAndLoggedIn());
+  // Backend mode transition rules:
+  // * can transit from IDLE to any other non-IDLE mode.
+  // * forbidden to transit from SYNC to any other mode, i.e. SYNC backend must
+  //   be explicitly shut down before backup/rollback starts.
+  // * can not transit out of ROLLBACK mode until rollback is finished
+  //   (successfully or unsuccessfully).
+  // * can not transit out of BACKUP mode until backup is finished
+  //   (successfully or unsuccessfully).
+  // * if backup is needed, can only transit to SYNC if backup is finished,
+
+  if (backend_mode_ == SYNC) {
+    LOG(DFATAL) << "Shouldn't switch from mode SYNC to mode " << mode;
+    return;
+  }
+
+  if (backend_mode_ == ROLLBACK ||
+      (backend_mode_ == BACKUP && !backup_finished_)) {
+    // Wait for rollback/backup to finish before start new backend.
+    return;
+  }
+
+  if (mode == SYNC && NeedBackup() && !backup_finished_) {
+    if (backend_mode_ != BACKUP)
+      backup_rollback_controller_.StartBackup();
+    return;
+  }
+
+  DVLOG(1) << "Start backend mode: " << mode;
+
+  if (backend_) {
+    if (mode == SYNC)
+      ShutdownImpl(syncer::SWITCH_MODE_SYNC);
+    else
+      ShutdownImpl(syncer::STOP_SYNC);
+  }
+
+  backend_mode_ = mode;
+
+  if (backend_mode_ == BACKUP)
+    backup_start_time_ = base::Time::Now();
+
+  if (backend_mode_ == SYNC && !backup_start_time_.is_null()) {
+    UMA_HISTOGRAM_MEDIUM_TIMES("Sync.FirstSyncDelayByBackup",
+                               base::Time::Now() - backup_start_time_);
+    backup_start_time_ = base::Time();
+  }
+
+  if (backend_mode_ == ROLLBACK)
+    ClearBrowsingDataSinceFirstSync();
+  else if (backend_mode_ == SYNC)
+    CheckSyncBackupIfNeeded();
+
+  base::FilePath sync_folder = backend_mode_ == SYNC ?
+      base::FilePath(kSyncDataFolderName) :
+      base::FilePath(kSyncBackupDataFolderName);
+
+  invalidation::InvalidationService* invalidator = NULL;
+  if (backend_mode_ == SYNC) {
+    invalidation::ProfileInvalidationProvider* provider =
+        invalidation::ProfileInvalidationProviderFactory::GetForProfile(
+            profile_);
+    if (provider)
+      invalidator = provider->GetInvalidationService();
+  }
+
+  directory_path_ = profile_->GetPath().Append(sync_folder);
 
-  DCHECK(!sync_disabled_by_admin_);
   backend_.reset(
       factory_->CreateSyncBackendHost(
           profile_->GetDebugName(),
           profile_,
-          sync_prefs_.AsWeakPtr()));
+          invalidator,
+          sync_prefs_.AsWeakPtr(),
+          sync_folder));
 
   // Initialize the backend.  Every time we start up a new SyncBackendHost,
   // we'll want to start from a fresh SyncDB, so delete any old one that might
   // be there.
-  InitializeBackend(!HasSyncSetupCompleted());
+  InitializeBackend(ShouldDeleteSyncFolder());
+
+  UpdateFirstSyncTimePref();
 }
 
 void ProfileSyncService::OnGetTokenSuccess(
@@ -619,7 +715,7 @@ void ProfileSyncService::OnGetTokenSuccess(
                               AUTH_ERROR_LIMIT);
   }
 
-  if (backend_)
+  if (HasSyncingBackend())
     backend_->UpdateCredentials(GetCredentials());
   else
     startup_controller_.TryStart();
@@ -634,6 +730,8 @@ void ProfileSyncService::OnGetTokenFailure(
   last_get_token_error_ = error;
   switch (error.state()) {
     case GoogleServiceAuthError::CONNECTION_FAILED:
+    case GoogleServiceAuthError::REQUEST_CANCELED:
+    case GoogleServiceAuthError::SERVICE_ERROR:
     case GoogleServiceAuthError::SERVICE_UNAVAILABLE: {
       // Transient error. Retry after some time.
       request_access_token_backoff_.InformOfRequest(false);
@@ -647,7 +745,6 @@ void ProfileSyncService::OnGetTokenFailure(
       NotifyObservers();
       break;
     }
-    case GoogleServiceAuthError::SERVICE_ERROR:
     case GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS: {
       if (!sync_prefs_.SyncHasAuthError()) {
         sync_prefs_.SetSyncAuthError(true);
@@ -658,6 +755,9 @@ void ProfileSyncService::OnGetTokenFailure(
       // Fallthrough.
     }
     default: {
+      if (error.state() != GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS) {
+        LOG(ERROR) << "Unexpected persistent error: " << error.ToString();
+      }
       // Show error to user.
       UpdateAuthErrorState(error);
     }
@@ -666,6 +766,11 @@ void ProfileSyncService::OnGetTokenFailure(
 
 void ProfileSyncService::OnRefreshTokenAvailable(
     const std::string& account_id) {
+  // TODO(vadimt): Remove ScopedTracker below once crbug.com/422460 is fixed.
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "422460 ProfileSyncService::OnRefreshTokenAvailable"));
+
   if (account_id == signin_->GetAccountIdToUse())
     OnRefreshTokensLoaded();
 }
@@ -691,7 +796,7 @@ void ProfileSyncService::OnRefreshTokensLoaded() {
   // Initialize the backend if sync is enabled. If the sync token was
   // not loaded, GetCredentials() will generate invalid credentials to
   // cause the backend to generate an auth error (crbug.com/121755).
-  if (backend_) {
+  if (HasSyncingBackend()) {
     RequestAccessToken();
   } else {
     startup_controller_.TryStart();
@@ -701,7 +806,7 @@ void ProfileSyncService::OnRefreshTokensLoaded() {
 void ProfileSyncService::Shutdown() {
   UnregisterAuthNotifications();
 
-  ShutdownImpl(browser_sync::SyncBackendHost::STOP);
+  ShutdownImpl(syncer::BROWSER_SHUTDOWN);
   if (sync_error_controller_) {
     // Destroy the SyncErrorController when the service shuts down for good.
     RemoveObserver(sync_error_controller_.get());
@@ -712,10 +817,18 @@ void ProfileSyncService::Shutdown() {
     sync_thread_->Stop();
 }
 
-void ProfileSyncService::ShutdownImpl(
-    browser_sync::SyncBackendHost::ShutdownOption option) {
-  if (!backend_)
+void ProfileSyncService::ShutdownImpl(syncer::ShutdownReason reason) {
+  if (!backend_) {
+    if (reason == syncer::ShutdownReason::DISABLE_SYNC && sync_thread_) {
+      // If the backend is already shut down when a DISABLE_SYNC happens,
+      // the data directory needs to be cleaned up here.
+      sync_thread_->message_loop()->PostTask(FROM_HERE,
+          base::Bind(&DeleteSyncDataFolder, directory_path_));
+    }
     return;
+  }
+
+  non_blocking_data_type_manager_.DisconnectSyncBackend();
 
   // First, we spin down the backend to stop change processing as soon as
   // possible.
@@ -727,14 +840,14 @@ void ProfileSyncService::ShutdownImpl(
   // change from a native model.  In that case, it will get applied to the sync
   // database (which doesn't get destroyed until we destroy the backend below)
   // as an unsynced change.  That will be persisted, and committed on restart.
-  if (data_type_manager_) {
-    if (data_type_manager_->state() != DataTypeManager::STOPPED) {
+  if (directory_data_type_manager_) {
+    if (directory_data_type_manager_->state() != DataTypeManager::STOPPED) {
       // When aborting as part of shutdown, we should expect an aborted sync
       // configure result, else we'll dcheck when we try to read the sync error.
       expect_sync_configuration_aborted_ = true;
-      data_type_manager_->Stop();
+      directory_data_type_manager_->Stop();
     }
-    data_type_manager_.reset();
+    directory_data_type_manager_.reset();
   }
 
   // Shutdown the migrator before the backend to ensure it doesn't pull a null
@@ -746,7 +859,7 @@ void ProfileSyncService::ShutdownImpl(
   // shutting it down.
   scoped_ptr<SyncBackendHost> doomed_backend(backend_.release());
   if (doomed_backend) {
-    sync_thread_ = doomed_backend->Shutdown(option);
+    sync_thread_ = doomed_backend->Shutdown(reason);
     doomed_backend.reset();
   }
   base::TimeDelta shutdown_time = base::Time::Now() - shutdown_start_time;
@@ -754,9 +867,25 @@ void ProfileSyncService::ShutdownImpl(
 
   weak_factory_.InvalidateWeakPtrs();
 
-  startup_controller_.Reset(GetRegisteredDataTypes());
+  if (backend_mode_ == SYNC)
+    startup_controller_.Reset(GetRegisteredDataTypes());
+
+  // Don't let backup block sync regardless backup succeeded or not.
+  if (backend_mode_ == BACKUP)
+    backup_finished_ = true;
+
+  // Sync could be blocked by rollback/backup. Post task to check whether sync
+  // should start after shutting down rollback/backup backend.
+  if ((backend_mode_ == ROLLBACK || backend_mode_ == BACKUP) &&
+      reason != syncer::SWITCH_MODE_SYNC &&
+      reason != syncer::BROWSER_SHUTDOWN) {
+    base::MessageLoop::current()->PostTask(
+        FROM_HERE, base::Bind(&ProfileSyncService::TryStartSyncAfterBackup,
+                              startup_controller_weak_factory_.GetWeakPtr()));
+  }
 
   // Clear various flags.
+  backend_mode_ = IDLE;
   expect_sync_configuration_aborted_ = false;
   is_auth_in_progress_ = false;
   backend_initialized_ = false;
@@ -779,7 +908,7 @@ void ProfileSyncService::DisableForUser() {
   // PSS clients don't think we're set up while we're shutting down.
   sync_prefs_.ClearPreferences();
   ClearUnrecoverableError();
-  ShutdownImpl(browser_sync::SyncBackendHost::DISABLE_AND_CLAIM_THREAD);
+  ShutdownImpl(syncer::DISABLE_SYNC);
 }
 
 bool ProfileSyncService::HasSyncSetupCompleted() const {
@@ -791,8 +920,7 @@ void ProfileSyncService::SetSyncSetupCompleted() {
 }
 
 void ProfileSyncService::UpdateLastSyncedTime() {
-  last_synced_time_ = base::Time::Now();
-  sync_prefs_.SetLastSyncedTime(last_synced_time_);
+  sync_prefs_.SetLastSyncedTime(base::Time::Now());
 }
 
 void ProfileSyncService::NotifyObservers() {
@@ -809,7 +937,9 @@ void ProfileSyncService::ClearStaleErrors() {
   ClearUnrecoverableError();
   last_actionable_error_ = SyncProtocolError();
   // Clear the data type errors as well.
-  failed_data_types_handler_.Reset();
+  if (directory_data_type_manager_.get())
+    directory_data_type_manager_->ResetDataTypeErrors();
+
 }
 
 void ProfileSyncService::ClearUnrecoverableError() {
@@ -819,7 +949,7 @@ void ProfileSyncService::ClearUnrecoverableError() {
 }
 
 void ProfileSyncService::RegisterNewDataType(syncer::ModelType data_type) {
-  if (data_type_controllers_.count(data_type) > 0)
+  if (directory_data_type_controllers_.count(data_type) > 0)
     return;
   NOTREACHED();
 }
@@ -846,7 +976,6 @@ void ProfileSyncService::OnUnrecoverableErrorImpl(
   UMA_HISTOGRAM_ENUMERATION(kSyncUnrecoverableErrorHistogram,
                             unrecoverable_error_reason_,
                             ERROR_REASON_LIMIT);
-  NotifyObservers();
   std::string location;
   from_here.Write(true, true, &location);
   LOG(ERROR)
@@ -858,41 +987,18 @@ void ProfileSyncService::OnUnrecoverableErrorImpl(
       base::Bind(&ProfileSyncService::ShutdownImpl,
                  weak_factory_.GetWeakPtr(),
                  delete_sync_database ?
-                     browser_sync::SyncBackendHost::DISABLE_AND_CLAIM_THREAD :
-                     browser_sync::SyncBackendHost::STOP_AND_CLAIM_THREAD));
+                     syncer::DISABLE_SYNC : syncer::STOP_SYNC));
 }
 
-// TODO(zea): Move this logic into the DataTypeController/DataTypeManager.
-void ProfileSyncService::DisableBrokenDatatype(
-    syncer::ModelType type,
-    const tracked_objects::Location& from_here,
-    std::string message) {
-  // First deactivate the type so that no further server changes are
-  // passed onto the change processor.
-  DeactivateDataType(type);
-
-  syncer::SyncError error(from_here,
-                          syncer::SyncError::DATATYPE_ERROR,
-                          message,
-                          type);
-
-  std::map<syncer::ModelType, syncer::SyncError> errors;
-  errors[type] = error;
-
-  // Update this before posting a task. So if a configure happens before
-  // the task that we are going to post, this type would still be disabled.
-  failed_data_types_handler_.UpdateFailedDataTypes(errors);
-
-  base::MessageLoop::current()->PostTask(FROM_HERE,
-      base::Bind(&ProfileSyncService::ReconfigureDatatypeManager,
-                 weak_factory_.GetWeakPtr()));
+void ProfileSyncService::ReenableDatatype(syncer::ModelType type) {
+  DCHECK(backend_initialized_);
+  directory_data_type_manager_->ReenableType(type);
 }
 
-void ProfileSyncService::OnBackendInitialized(
-    const syncer::WeakHandle<syncer::JsBackend>& js_backend,
-    const syncer::WeakHandle<syncer::DataTypeDebugInfoListener>&
-        debug_info_listener,
-    bool success) {
+void ProfileSyncService::UpdateBackendInitUMA(bool success) {
+  if (backend_mode_ != SYNC)
+    return;
+
   is_first_time_sync_configure_ = !HasSyncSetupCompleted();
 
   if (is_first_time_sync_configure_) {
@@ -909,34 +1015,26 @@ void ProfileSyncService::OnBackendInitialized(
   } else {
     UMA_HISTOGRAM_LONG_TIMES("Sync.BackendInitializeRestoreTime", delta);
   }
+}
 
-  if (!success) {
-    // Something went unexpectedly wrong.  Play it safe: stop syncing at once
-    // and surface error UI to alert the user sync has stopped.
-    // Keep the directory around for now so that on restart we will retry
-    // again and potentially succeed in presence of transient file IO failures
-    // or permissions issues, etc.
-    //
-    // TODO(rlarocque): Consider making this UnrecoverableError less special.
-    // Unlike every other UnrecoverableError, it does not delete our sync data.
-    // This exception made sense at the time it was implemented, but our new
-    // directory corruption recovery mechanism makes it obsolete.  By the time
-    // we get here, we will have already tried and failed to delete the
-    // directory.  It would be no big deal if we tried to delete it again.
-    OnInternalUnrecoverableError(FROM_HERE,
-                                 "BackendInitialize failure",
-                                 false,
-                                 ERROR_REASON_BACKEND_INIT_FAILURE);
-    return;
+void ProfileSyncService::PostBackendInitialization() {
+  // Never get here for backup / restore.
+  DCHECK_EQ(backend_mode_, SYNC);
+
+  if (last_backup_time_) {
+    DCHECK(device_info_sync_service_);
+    device_info_sync_service_->UpdateLocalDeviceBackupTime(*last_backup_time_);
   }
 
-  backend_initialized_ = true;
+  if (protocol_event_observers_.might_have_observers()) {
+    backend_->RequestBufferedProtocolEventsAndEnableForwarding();
+  }
 
-  sync_js_controller_.AttachJsBackend(js_backend);
-  debug_info_listener_ = debug_info_listener;
+  non_blocking_data_type_manager_.ConnectSyncBackend(
+      backend_->GetSyncContextProxy());
 
-  if (protocol_event_observers_.might_have_observers()) {
-    backend_->SetForwardProtocolEvents(true);
+  if (type_debug_info_observers_.might_have_observers()) {
+    backend_->EnableDirectoryTypeDebugInfoForwarding();
   }
 
   // If we have a cached passphrase use it to decrypt/encrypt data now that the
@@ -945,9 +1043,9 @@ void ProfileSyncService::OnBackendInitialized(
   ConsumeCachedPassphraseIfPossible();
 
   // The very first time the backend initializes is effectively the first time
-  // we can say we successfully "synced".  last_synced_time_ will only be null
-  // in this case, because the pref wasn't restored on StartUp.
-  if (last_synced_time_.is_null()) {
+  // we can say we successfully "synced".  LastSyncedTime will only be null in
+  // this case, because the pref wasn't restored on StartUp.
+  if (sync_prefs_.GetLastSyncedTime().is_null()) {
     UpdateLastSyncedTime();
   }
 
@@ -970,21 +1068,73 @@ void ProfileSyncService::OnBackendInitialized(
   NotifyObservers();
 }
 
+void ProfileSyncService::OnBackendInitialized(
+    const syncer::WeakHandle<syncer::JsBackend>& js_backend,
+    const syncer::WeakHandle<syncer::DataTypeDebugInfoListener>&
+        debug_info_listener,
+    const std::string& cache_guid,
+    bool success) {
+  UpdateBackendInitUMA(success);
+
+  if (!success) {
+    // Something went unexpectedly wrong.  Play it safe: stop syncing at once
+    // and surface error UI to alert the user sync has stopped.
+    // Keep the directory around for now so that on restart we will retry
+    // again and potentially succeed in presence of transient file IO failures
+    // or permissions issues, etc.
+    //
+    // TODO(rlarocque): Consider making this UnrecoverableError less special.
+    // Unlike every other UnrecoverableError, it does not delete our sync data.
+    // This exception made sense at the time it was implemented, but our new
+    // directory corruption recovery mechanism makes it obsolete.  By the time
+    // we get here, we will have already tried and failed to delete the
+    // directory.  It would be no big deal if we tried to delete it again.
+    OnInternalUnrecoverableError(FROM_HERE,
+                                 "BackendInitialize failure",
+                                 false,
+                                 ERROR_REASON_BACKEND_INIT_FAILURE);
+    return;
+  }
+
+  backend_initialized_ = true;
+
+  sync_js_controller_.AttachJsBackend(js_backend);
+  debug_info_listener_ = debug_info_listener;
+
+  SigninClient* signin_client =
+      ChromeSigninClientFactory::GetForProfile(profile_);
+  DCHECK(signin_client);
+  std::string signin_scoped_device_id =
+      signin_client->GetSigninScopedDeviceId();
+
+  // Initialize local device info.
+  local_device_->Initialize(cache_guid, signin_scoped_device_id);
+
+  DVLOG(1) << "Setting preferred types for non-blocking DTM";
+  non_blocking_data_type_manager_.SetPreferredTypes(GetPreferredDataTypes());
+
+  // Give the DataTypeControllers a handle to the now initialized backend
+  // as a UserShare.
+  for (DataTypeController::TypeMap::iterator it =
+       directory_data_type_controllers_.begin();
+       it != directory_data_type_controllers_.end(); ++it) {
+    it->second->OnUserShareReady(GetUserShare());
+  }
+
+  if (backend_mode_ == BACKUP || backend_mode_ == ROLLBACK)
+    ConfigureDataTypeManager();
+  else
+    PostBackendInitialization();
+}
+
 void ProfileSyncService::OnSyncCycleCompleted() {
   UpdateLastSyncedTime();
-  if (IsSessionsDataTypeControllerRunning()) {
+  if (IsDataTypeControllerRunning(syncer::SESSIONS)) {
     // Trigger garbage collection of old sessions now that we've downloaded
     // any new session data.
-    if (sessions_sync_manager_) {
-      // Sessions V2.
-      base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
-          &browser_sync::SessionsSyncManager::DoGarbageCollection,
-              base::AsWeakPtr(sessions_sync_manager_.get())));
-    } else {
-      base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
-          &browser_sync::SessionModelAssociator::DeleteStaleSessions,
-              GetSessionModelAssociatorDeprecated()->AsWeakPtr()));
-    }
+    base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
+        &SessionsSyncManager::DoGarbageCollection,
+            base::AsWeakPtr(sessions_sync_manager_.get())));
   }
   DVLOG(2) << "Notifying observers sync cycle completed";
   NotifySyncCycleCompleted();
@@ -998,63 +1148,31 @@ void ProfileSyncService::OnExperimentsChanged(
   current_experiments_ = experiments;
 
   // Handle preference-backed experiments first.
-  if (experiments.gcm_channel_state != syncer::Experiments::UNSET) {
-    profile()->GetPrefs()->SetBoolean(prefs::kGCMChannelEnabled,
-                                      experiments.gcm_channel_state ==
-                                          syncer::Experiments::ENABLED);
-    gcm::GCMProfileService* gcm_profile_service =
-        gcm::GCMProfileServiceFactory::GetForProfile(profile());
-    if (gcm_profile_service) {
-      if (experiments.gcm_channel_state == syncer::Experiments::SUPPRESSED)
-        gcm_profile_service->Stop();
-      else
-        gcm_profile_service->Start();
-    }
+  if (experiments.gcm_channel_state == syncer::Experiments::SUPPRESSED) {
+    profile()->GetPrefs()->SetBoolean(prefs::kGCMChannelEnabled, false);
+    gcm::GCMProfileServiceFactory::GetForProfile(profile())->driver()
+        ->Disable();
   } else {
     profile()->GetPrefs()->ClearPref(prefs::kGCMChannelEnabled);
+    gcm::GCMProfileServiceFactory::GetForProfile(profile())->driver()
+        ->Enable();
   }
 
   profile()->GetPrefs()->SetBoolean(prefs::kInvalidationServiceUseGCMChannel,
                                     experiments.gcm_invalidations_enabled);
 
-  int bookmarks_experiment_state_before = profile_->GetPrefs()->GetInteger(
-      sync_driver::prefs::kEnhancedBookmarksExperimentEnabled);
-  // kEnhancedBookmarksExperiment flag could have values "", "1" and "0".
-  // "" and "1" means experiment is enabled.
-  if ((CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
-           switches::kEnhancedBookmarksExperiment) != "0")) {
-    profile_->GetPrefs()->SetInteger(
-        sync_driver::prefs::kEnhancedBookmarksExperimentEnabled,
-        experiments.enhanced_bookmarks_enabled ? kBookmarksExperimentEnabled
-                                               : kNoBookmarksExperiment);
+  if (experiments.enhanced_bookmarks_enabled) {
     profile_->GetPrefs()->SetString(
         sync_driver::prefs::kEnhancedBookmarksExtensionId,
         experiments.enhanced_bookmarks_ext_id);
   } else {
-    // User opt-out from chrome://flags
-    if (experiments.enhanced_bookmarks_enabled) {
-      profile_->GetPrefs()->SetInteger(
-          sync_driver::prefs::kEnhancedBookmarksExperimentEnabled,
-          kBookmarksExperimentEnabledUserOptOut);
-      // Keep extension id up-to-date in case will opt-in later.
-      profile_->GetPrefs()->SetString(
-          sync_driver::prefs::kEnhancedBookmarksExtensionId,
-          experiments.enhanced_bookmarks_ext_id);
-    } else {
-      profile_->GetPrefs()->ClearPref(
-          sync_driver::prefs::kEnhancedBookmarksExperimentEnabled);
-      profile_->GetPrefs()->ClearPref(
-          sync_driver::prefs::kEnhancedBookmarksExtensionId);
-    }
-  }
-  BookmarksExperimentState bookmarks_experiment_state =
-      static_cast<BookmarksExperimentState>(profile_->GetPrefs()->GetInteger(
-          sync_driver::prefs::kEnhancedBookmarksExperimentEnabled));
-  // If bookmark experiment state was changed update about flags experiment.
-  if (bookmarks_experiment_state_before != bookmarks_experiment_state) {
-    UpdateBookmarksExperiment(g_browser_process->local_state(),
-                              bookmarks_experiment_state);
+    profile_->GetPrefs()->ClearPref(
+        sync_driver::prefs::kEnhancedBookmarksExtensionId);
   }
+  UpdateBookmarksExperimentState(
+      profile_->GetPrefs(), g_browser_process->local_state(), true,
+      experiments.enhanced_bookmarks_enabled ? BOOKMARKS_EXPERIMENT_ENABLED :
+                                               BOOKMARKS_EXPERIMENT_NONE);
 
   // If this is a first time sync for a client, this will be called before
   // OnBackendInitialized() to ensure the new datatypes are available at sync
@@ -1213,14 +1331,16 @@ void ProfileSyncService::OnPassphraseRequired(
            << syncer::PassphraseRequiredReasonToString(reason);
   passphrase_required_reason_ = reason;
 
-  const syncer::ModelTypeSet types = GetPreferredDataTypes();
-  if (data_type_manager_) {
+  const syncer::ModelTypeSet types = GetPreferredDirectoryDataTypes();
+  if (directory_data_type_manager_) {
     // Reconfigure without the encrypted types (excluded implicitly via the
     // failed datatypes handler).
-    data_type_manager_->Configure(types,
-                                  syncer::CONFIGURE_REASON_CRYPTO);
+    directory_data_type_manager_->Configure(types,
+                                            syncer::CONFIGURE_REASON_CRYPTO);
   }
 
+  // TODO(rlarocque): Support non-blocking types.  http://crbug.com/351005.
+
   // Notify observers that the passphrase status may have changed.
   NotifyObservers();
 }
@@ -1241,13 +1361,15 @@ void ProfileSyncService::OnPassphraseAccepted() {
 
   // Make sure the data types that depend on the passphrase are started at
   // this time.
-  const syncer::ModelTypeSet types = GetPreferredDataTypes();
-  if (data_type_manager_) {
+  const syncer::ModelTypeSet types = GetPreferredDirectoryDataTypes();
+  if (directory_data_type_manager_) {
     // Re-enable any encrypted types if necessary.
-    data_type_manager_->Configure(types,
-                                  syncer::CONFIGURE_REASON_CRYPTO);
+    directory_data_type_manager_->Configure(types,
+                                            syncer::CONFIGURE_REASON_CRYPTO);
   }
 
+  // TODO(rlarocque): Support non-blocking types.  http://crbug.com/351005.
+
   NotifyObservers();
 }
 
@@ -1256,20 +1378,14 @@ void ProfileSyncService::OnEncryptedTypesChanged(
     bool encrypt_everything) {
   encrypted_types_ = encrypted_types;
   encrypt_everything_ = encrypt_everything;
+  DCHECK(encrypt_everything_allowed_ || !encrypt_everything_);
   DVLOG(1) << "Encrypted types changed to "
            << syncer::ModelTypeSetToString(encrypted_types_)
            << " (encrypt everything is set to "
            << (encrypt_everything_ ? "true" : "false") << ")";
   DCHECK(encrypted_types_.Has(syncer::PASSWORDS));
 
-  // If sessions are encrypted, full history sync is not possible, and
-  // delete directives are unnecessary.
-  if (GetActiveDataTypes().Has(syncer::HISTORY_DELETE_DIRECTIVES) &&
-      encrypted_types_.Has(syncer::SESSIONS)) {
-    DisableBrokenDatatype(syncer::HISTORY_DELETE_DIRECTIVES,
-                          FROM_HERE,
-                          "Delete directives not supported with encryption.");
-  }
+  NotifyObservers();
 }
 
 void ProfileSyncService::OnEncryptionComplete() {
@@ -1285,7 +1401,7 @@ void ProfileSyncService::OnEncryptionComplete() {
 void ProfileSyncService::OnMigrationNeededForTypes(
     syncer::ModelTypeSet types) {
   DCHECK(backend_initialized_);
-  DCHECK(data_type_manager_.get());
+  DCHECK(directory_data_type_manager_.get());
 
   // Migrator must be valid, because we don't sync until it is created and this
   // callback originates from a sync cycle.
@@ -1314,36 +1430,77 @@ void ProfileSyncService::OnActionableError(const SyncProtocolError& error) {
                                    true,
                                    ERROR_REASON_ACTIONABLE_ERROR);
       break;
+    case syncer::DISABLE_SYNC_AND_ROLLBACK:
+      backup_rollback_controller_.OnRollbackReceived();
+      // Fall through to shutdown backend and sign user out.
     case syncer::DISABLE_SYNC_ON_CLIENT:
       StopSyncingPermanently();
 #if !defined(OS_CHROMEOS)
       // On desktop Chrome, sign out the user after a dashboard clear.
       // Skip sign out on ChromeOS/Android.
-      if (!startup_controller_.auto_start_enabled())
-        SigninManagerFactory::GetForProfile(profile_)->SignOut();
+      if (!startup_controller_.auto_start_enabled()) {
+        SigninManagerFactory::GetForProfile(profile_)->SignOut(
+            signin_metrics::SERVER_FORCED_DISABLE);
+      }
 #endif
       break;
+    case syncer::ROLLBACK_DONE:
+      backup_rollback_controller_.OnRollbackDone();
+      break;
     case syncer::STOP_SYNC_FOR_DISABLED_ACCOUNT:
       // Sync disabled by domain admin. we should stop syncing until next
       // restart.
       sync_disabled_by_admin_ = true;
-      ShutdownImpl(browser_sync::SyncBackendHost::DISABLE_AND_CLAIM_THREAD);
+      ShutdownImpl(syncer::DISABLE_SYNC);
       break;
     default:
       NOTREACHED();
   }
   NotifyObservers();
+
+  if (error.action == syncer::DISABLE_SYNC_ON_CLIENT ||
+      (error.action == syncer::DISABLE_SYNC_AND_ROLLBACK &&
+          !backup_rollback_controller_.StartRollback())) {
+    // Clean up backup data for sign-out only or when rollback is disabled.
+    CleanUpBackup();
+  } else if (error.action == syncer::ROLLBACK_DONE) {
+    // Shut down ROLLBACK backend and delete backup DB.
+    ShutdownImpl(syncer::DISABLE_SYNC);
+    sync_prefs_.ClearFirstSyncTime();
+  }
 }
 
 void ProfileSyncService::OnConfigureDone(
-    const browser_sync::DataTypeManager::ConfigureResult& result) {
+    const DataTypeManager::ConfigureResult& result) {
+  configure_status_ = result.status;
+  data_type_status_table_ = result.data_type_status_table;
+
+  if (backend_mode_ != SYNC) {
+    if (configure_status_ == DataTypeManager::OK) {
+      StartSyncingWithServer();
+
+      // Backup is done after models are associated.
+      if (backend_mode_ == BACKUP)
+        backup_finished_ = true;
+
+      // Asynchronously check whether sync needs to start.
+      base::MessageLoop::current()->PostTask(
+          FROM_HERE, base::Bind(&ProfileSyncService::TryStartSyncAfterBackup,
+                                startup_controller_weak_factory_.GetWeakPtr()));
+    } else if (!expect_sync_configuration_aborted_) {
+      DVLOG(1) << "Backup/rollback backend failed to configure.";
+      ShutdownImpl(syncer::STOP_SYNC);
+    }
+
+    return;
+  }
+
   // We should have cleared our cached passphrase before we get here (in
   // OnBackendInitialized()).
   DCHECK(cached_passphrase_.empty());
 
   if (!sync_configure_start_time_.is_null()) {
-    if (result.status == DataTypeManager::OK ||
-        result.status == DataTypeManager::PARTIAL_SUCCESS) {
+    if (result.status == DataTypeManager::OK) {
       base::Time sync_configure_stop_time = base::Time::Now();
       base::TimeDelta delta = sync_configure_stop_time -
           sync_configure_start_time_;
@@ -1363,13 +1520,11 @@ void ProfileSyncService::OnConfigureDone(
       content::Source<ProfileSyncService>(this),
       content::NotificationService::NoDetails());
 
-  configure_status_ = result.status;
   DVLOG(1) << "PSS OnConfigureDone called with status: " << configure_status_;
   // The possible status values:
   //    ABORT - Configuration was aborted. This is not an error, if
   //            initiated by user.
-  //    OK - Everything succeeded.
-  //    PARTIAL_SUCCESS - Some datatypes failed to start.
+  //    OK - Some or all types succeeded.
   //    Everything else is an UnrecoverableError. So treat it as such.
 
   // First handle the abort case.
@@ -1381,18 +1536,18 @@ void ProfileSyncService::OnConfigureDone(
   }
 
   // Handle unrecoverable error.
-  if (configure_status_ != DataTypeManager::OK &&
-      configure_status_ != DataTypeManager::PARTIAL_SUCCESS) {
+  if (configure_status_ != DataTypeManager::OK) {
     // Something catastrophic had happened. We should only have one
     // error representing it.
-    DCHECK_EQ(result.failed_data_types.size(),
-              static_cast<unsigned int>(1));
-    syncer::SyncError error = result.failed_data_types.begin()->second;
+    syncer::SyncError error =
+        data_type_status_table_.GetUnrecoverableError();
     DCHECK(error.IsSet());
     std::string message =
         "Sync configuration failed with status " +
         DataTypeManager::ConfigureStatusToString(configure_status_) +
-        " during " + syncer::ModelTypeToString(error.model_type()) +
+        " caused by " +
+        syncer::ModelTypeSetToString(
+            data_type_status_table_.GetUnrecoverableErrorTypes()) +
         ": " + error.message();
     LOG(ERROR) << "ProfileSyncService error: " << message;
     OnInternalUnrecoverableError(error.location(),
@@ -1424,14 +1579,6 @@ void ProfileSyncService::OnConfigureDone(
   }
 }
 
-void ProfileSyncService::OnConfigureRetry() {
-  // We should have cleared our cached passphrase before we get here (in
-  // OnBackendInitialized()).
-  DCHECK(cached_passphrase_.empty());
-
-  OnSyncConfigureRetry();
-}
-
 void ProfileSyncService::OnConfigureStart() {
   sync_configure_start_time_ = base::Time::Now();
   NotifyObservers();
@@ -1443,13 +1590,18 @@ ProfileSyncService::SyncStatusSummary
     return UNRECOVERABLE_ERROR;
   } else if (!backend_) {
     return NOT_ENABLED;
+  } else if (backend_mode_ == BACKUP) {
+    return BACKUP_USER_DATA;
+  } else if (backend_mode_ == ROLLBACK) {
+    return ROLLBACK_USER_DATA;
   } else if (backend_.get() && !HasSyncSetupCompleted()) {
     return SETUP_INCOMPLETE;
-  } else if (backend_.get() && HasSyncSetupCompleted() &&
-             data_type_manager_.get() &&
-             data_type_manager_->state() != DataTypeManager::CONFIGURED) {
+  } else if (
+      backend_.get() && HasSyncSetupCompleted() &&
+      directory_data_type_manager_.get() &&
+      directory_data_type_manager_->state() == DataTypeManager::STOPPED) {
     return DATATYPES_NOT_INITIALIZED;
-  } else if (ShouldPushChanges()) {
+  } else if (SyncActive()) {
     return INITIALIZED;
   }
   return UNKNOWN_ERROR;
@@ -1457,6 +1609,11 @@ ProfileSyncService::SyncStatusSummary
 
 std::string ProfileSyncService::QuerySyncStatusSummaryString() {
   SyncStatusSummary status = QuerySyncStatusSummary();
+
+  std::string config_status_str =
+      configure_status_ != DataTypeManager::UNKNOWN ?
+          DataTypeManager::ConfigureStatusToString(configure_status_) : "";
+
   switch (status) {
     case UNRECOVERABLE_ERROR:
       return "Unrecoverable error detected";
@@ -1468,6 +1625,10 @@ std::string ProfileSyncService::QuerySyncStatusSummaryString() {
       return "Datatypes not fully initialized";
     case INITIALIZED:
       return "Sync service initialized";
+    case BACKUP_USER_DATA:
+      return "Backing-up user data. Status: " + config_status_str;
+    case ROLLBACK_USER_DATA:
+      return "Restoring user data. Status: " + config_status_str;
     default:
       return "Status unknown: Internal error?";
   }
@@ -1512,15 +1673,30 @@ void ProfileSyncService::SetSetupInProgress(bool setup_in_progress) {
     return;
 
   startup_controller_.set_setup_in_progress(setup_in_progress);
-  if (!setup_in_progress && sync_initialized())
+  if (!setup_in_progress && backend_initialized())
     ReconfigureDatatypeManager();
   NotifyObservers();
 }
 
-bool ProfileSyncService::sync_initialized() const {
+bool ProfileSyncService::SyncActive() const {
+  return backend_initialized_ && backend_mode_ == SYNC &&
+         directory_data_type_manager_ &&
+         directory_data_type_manager_->state() != DataTypeManager::STOPPED;
+}
+
+bool ProfileSyncService::backend_initialized() const {
   return backend_initialized_;
 }
 
+ProfileSyncService::BackendMode ProfileSyncService::backend_mode() const {
+  return backend_mode_;
+}
+
+bool ProfileSyncService::ConfigurationDone() const {
+  return directory_data_type_manager_ &&
+      directory_data_type_manager_->state() == DataTypeManager::CONFIGURED;
+}
+
 bool ProfileSyncService::waiting_for_auth() const {
   return is_auth_in_progress_;
 }
@@ -1546,16 +1722,18 @@ bool ProfileSyncService::IsPassphraseRequiredForDecryption() const {
 }
 
 base::string16 ProfileSyncService::GetLastSyncedTimeString() const {
-  if (last_synced_time_.is_null())
+  const base::Time last_synced_time = sync_prefs_.GetLastSyncedTime();
+  if (last_synced_time.is_null())
     return l10n_util::GetStringUTF16(IDS_SYNC_TIME_NEVER);
 
-  base::TimeDelta last_synced = base::Time::Now() - last_synced_time_;
+  base::TimeDelta time_since_last_sync = base::Time::Now() - last_synced_time;
 
-  if (last_synced < base::TimeDelta::FromMinutes(1))
+  if (time_since_last_sync < base::TimeDelta::FromMinutes(1))
     return l10n_util::GetStringUTF16(IDS_SYNC_TIME_JUST_NOW);
 
   return ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_ELAPSED,
-                                ui::TimeFormat::LENGTH_SHORT, last_synced);
+                                ui::TimeFormat::LENGTH_SHORT,
+                                time_since_last_sync);
 }
 
 void ProfileSyncService::UpdateSelectedTypesHistogram(
@@ -1568,20 +1746,21 @@ void ProfileSyncService::UpdateSelectedTypesHistogram(
   // Only log the data types that are shown in the sync settings ui.
   // Note: the order of these types must match the ordering of
   // the respective types in ModelType
-const browser_sync::user_selectable_type::UserSelectableSyncType
+  const sync_driver::user_selectable_type::UserSelectableSyncType
       user_selectable_types[] = {
-    browser_sync::user_selectable_type::BOOKMARKS,
-    browser_sync::user_selectable_type::PREFERENCES,
-    browser_sync::user_selectable_type::PASSWORDS,
-    browser_sync::user_selectable_type::AUTOFILL,
-    browser_sync::user_selectable_type::THEMES,
-    browser_sync::user_selectable_type::TYPED_URLS,
-    browser_sync::user_selectable_type::EXTENSIONS,
-    browser_sync::user_selectable_type::APPS,
-    browser_sync::user_selectable_type::PROXY_TABS
+    sync_driver::user_selectable_type::BOOKMARKS,
+    sync_driver::user_selectable_type::PREFERENCES,
+    sync_driver::user_selectable_type::PASSWORDS,
+    sync_driver::user_selectable_type::AUTOFILL,
+    sync_driver::user_selectable_type::THEMES,
+    sync_driver::user_selectable_type::TYPED_URLS,
+    sync_driver::user_selectable_type::EXTENSIONS,
+    sync_driver::user_selectable_type::APPS,
+    sync_driver::user_selectable_type::WIFI_CREDENTIAL,
+    sync_driver::user_selectable_type::PROXY_TABS,
   };
 
-  COMPILE_ASSERT(32 == syncer::MODEL_TYPE_COUNT, UpdateCustomConfigHistogram);
+  COMPILE_ASSERT(33 == syncer::MODEL_TYPE_COUNT, UpdateCustomConfigHistogram);
 
   if (!sync_everything) {
     const syncer::ModelTypeSet current_types = GetPreferredDataTypes();
@@ -1600,7 +1779,7 @@ const browser_sync::user_selectable_type::UserSelectableSyncType
         UMA_HISTOGRAM_ENUMERATION(
             "Sync.CustomSync",
             user_selectable_types[i],
-            browser_sync::user_selectable_type::SELECTABLE_DATATYPE_COUNT + 1);
+            sync_driver::user_selectable_type::SELECTABLE_DATATYPE_COUNT + 1);
       }
     }
   }
@@ -1609,7 +1788,7 @@ const browser_sync::user_selectable_type::UserSelectableSyncType
 #if defined(OS_CHROMEOS)
 void ProfileSyncService::RefreshSpareBootstrapToken(
     const std::string& passphrase) {
-  browser_sync::SystemEncryptor encryptor;
+  sync_driver::SystemEncryptor encryptor;
   syncer::Cryptographer temp_cryptographer(&encryptor);
   // The first 2 params (hostname and username) doesn't have any effect here.
   syncer::KeyParams key_params = {"localhost", "dummy", passphrase};
@@ -1634,16 +1813,10 @@ void ProfileSyncService::OnUserChoseDatatypes(
   UpdateSelectedTypesHistogram(sync_everything, chosen_types);
   sync_prefs_.SetKeepEverythingSynced(sync_everything);
 
-  failed_data_types_handler_.Reset();
-  if (GetActiveDataTypes().Has(syncer::HISTORY_DELETE_DIRECTIVES) &&
-      encrypted_types_.Has(syncer::SESSIONS)) {
-    DisableBrokenDatatype(syncer::HISTORY_DELETE_DIRECTIVES,
-                          FROM_HERE,
-                          "Delete directives not supported with encryption.");
-  }
+  if (directory_data_type_manager_.get())
+    directory_data_type_manager_->ResetDataTypeErrors();
   ChangePreferredDataTypes(chosen_types);
   AcknowledgeSyncedTypes();
-  NotifyObservers();
 }
 
 void ProfileSyncService::ChangePreferredDataTypes(
@@ -1658,12 +1831,18 @@ void ProfileSyncService::ChangePreferredDataTypes(
 
   // Now reconfigure the DTM.
   ReconfigureDatatypeManager();
+
+  // TODO(rlarocque): Reconfigure the NonBlockingDataTypeManager, too.  Blocked
+  // on crbug.com/368834.  Until that bug is fixed, it's difficult to tell
+  // which types should be enabled and when.
 }
 
 syncer::ModelTypeSet ProfileSyncService::GetActiveDataTypes() const {
+  if (!SyncActive() || !ConfigurationDone())
+    return syncer::ModelTypeSet();
   const syncer::ModelTypeSet preferred_types = GetPreferredDataTypes();
   const syncer::ModelTypeSet failed_types =
-      failed_data_types_handler_.GetFailedTypes();
+      data_type_status_table_.GetFailedTypes();
   return Difference(preferred_types, failed_types);
 }
 
@@ -1671,21 +1850,58 @@ syncer::ModelTypeSet ProfileSyncService::GetPreferredDataTypes() const {
   const syncer::ModelTypeSet registered_types = GetRegisteredDataTypes();
   const syncer::ModelTypeSet preferred_types =
       sync_prefs_.GetPreferredDataTypes(registered_types);
-  return preferred_types;
+  const syncer::ModelTypeSet enforced_types =
+      Intersection(GetDataTypesFromPreferenceProviders(), registered_types);
+  return Union(preferred_types, enforced_types);
+}
+
+syncer::ModelTypeSet
+ProfileSyncService::GetPreferredDirectoryDataTypes() const {
+  const syncer::ModelTypeSet registered_directory_types =
+      GetRegisteredDirectoryDataTypes();
+  const syncer::ModelTypeSet preferred_types =
+      sync_prefs_.GetPreferredDataTypes(registered_directory_types);
+  const syncer::ModelTypeSet enforced_types =
+      Intersection(GetDataTypesFromPreferenceProviders(),
+                   registered_directory_types);
+  return Union(preferred_types, enforced_types);
+}
+
+syncer::ModelTypeSet
+ProfileSyncService::GetPreferredNonBlockingDataTypes() const {
+  return sync_prefs_.GetPreferredDataTypes(GetRegisteredNonBlockingDataTypes());
+}
+
+syncer::ModelTypeSet ProfileSyncService::GetForcedDataTypes() const {
+  // TODO(treib,zea): When SyncPrefs also implements SyncTypePreferenceProvider,
+  // we'll need another way to distinguish user-choosable types from
+  // programmatically-enabled types.
+  return GetDataTypesFromPreferenceProviders();
 }
 
 syncer::ModelTypeSet ProfileSyncService::GetRegisteredDataTypes() const {
+  return Union(GetRegisteredDirectoryDataTypes(),
+               GetRegisteredNonBlockingDataTypes());
+}
+
+syncer::ModelTypeSet
+ProfileSyncService::GetRegisteredDirectoryDataTypes() const {
   syncer::ModelTypeSet registered_types;
-  // The data_type_controllers_ are determined by command-line flags; that's
-  // effectively what controls the values returned here.
+  // The directory_data_type_controllers_ are determined by command-line flags;
+  // that's effectively what controls the values returned here.
   for (DataTypeController::TypeMap::const_iterator it =
-       data_type_controllers_.begin();
-       it != data_type_controllers_.end(); ++it) {
+       directory_data_type_controllers_.begin();
+       it != directory_data_type_controllers_.end(); ++it) {
     registered_types.Put(it->first);
   }
   return registered_types;
 }
 
+syncer::ModelTypeSet
+ProfileSyncService::GetRegisteredNonBlockingDataTypes() const {
+  return non_blocking_data_type_manager_.GetRegisteredTypes();
+}
+
 bool ProfileSyncService::IsUsingSecondaryPassphrase() const {
   syncer::PassphraseType passphrase_type = GetPassphraseType();
   return passphrase_type == syncer::FROZEN_IMPLICIT_PASSPHRASE ||
@@ -1707,12 +1923,13 @@ bool ProfileSyncService::IsCryptographerReady(
 
 void ProfileSyncService::ConfigurePriorityDataTypes() {
   const syncer::ModelTypeSet priority_types =
-      Intersection(GetPreferredDataTypes(), syncer::PriorityUserTypes());
+      Intersection(GetPreferredDirectoryDataTypes(),
+                   syncer::PriorityUserTypes());
   if (!priority_types.Empty()) {
     const syncer::ConfigureReason reason = HasSyncSetupCompleted() ?
         syncer::CONFIGURE_REASON_RECONFIGURATION :
         syncer::CONFIGURE_REASON_NEW_CLIENT;
-    data_type_manager_->Configure(priority_types, reason);
+    directory_data_type_manager_->Configure(priority_types, reason);
   }
 }
 
@@ -1722,46 +1939,51 @@ void ProfileSyncService::ConfigureDataTypeManager() {
   // start syncing data until the user is done configuring encryption options,
   // etc. ReconfigureDatatypeManager() will get called again once the UI calls
   // SetSetupInProgress(false).
-  if (startup_controller_.setup_in_progress())
+  if (backend_mode_ == SYNC && startup_controller_.setup_in_progress())
     return;
 
   bool restart = false;
-  if (!data_type_manager_) {
+  if (!directory_data_type_manager_) {
     restart = true;
-    data_type_manager_.reset(
+    directory_data_type_manager_.reset(
         factory_->CreateDataTypeManager(debug_info_listener_,
-                                        &data_type_controllers_,
+                                        &directory_data_type_controllers_,
                                         this,
                                         backend_.get(),
-                                        this,
-                                        &failed_data_types_handler_));
+                                        this));
 
     // We create the migrator at the same time.
     migrator_.reset(
         new browser_sync::BackendMigrator(
             profile_->GetDebugName(), GetUserShare(),
-            this, data_type_manager_.get(),
+            this, directory_data_type_manager_.get(),
             base::Bind(&ProfileSyncService::StartSyncingWithServer,
                        base::Unretained(this))));
   }
 
-  const syncer::ModelTypeSet types = GetPreferredDataTypes();
+  syncer::ModelTypeSet types;
   syncer::ConfigureReason reason = syncer::CONFIGURE_REASON_UNKNOWN;
-  if (!HasSyncSetupCompleted()) {
-    reason = syncer::CONFIGURE_REASON_NEW_CLIENT;
-  } else if (restart) {
-    // Datatype downloads on restart are generally due to newly supported
-    // datatypes (although it's also possible we're picking up where a failed
-    // previous configuration left off).
-    // TODO(sync): consider detecting configuration recovery and setting
-    // the reason here appropriately.
-    reason = syncer::CONFIGURE_REASON_NEWLY_ENABLED_DATA_TYPE;
+  if (backend_mode_ == BACKUP || backend_mode_ == ROLLBACK) {
+    types = syncer::BackupTypes();
+    reason = syncer::CONFIGURE_REASON_BACKUP_ROLLBACK;
   } else {
-    // The user initiated a reconfiguration (either to add or remove types).
-    reason = syncer::CONFIGURE_REASON_RECONFIGURATION;
+    types = GetPreferredDirectoryDataTypes();
+    if (!HasSyncSetupCompleted()) {
+      reason = syncer::CONFIGURE_REASON_NEW_CLIENT;
+    } else if (restart) {
+      // Datatype downloads on restart are generally due to newly supported
+      // datatypes (although it's also possible we're picking up where a failed
+      // previous configuration left off).
+      // TODO(sync): consider detecting configuration recovery and setting
+      // the reason here appropriately.
+      reason = syncer::CONFIGURE_REASON_NEWLY_ENABLED_DATA_TYPE;
+    } else {
+      // The user initiated a reconfiguration (either to add or remove types).
+      reason = syncer::CONFIGURE_REASON_RECONFIGURATION;
+    }
   }
 
-  data_type_manager_->Configure(types, reason);
+  directory_data_type_manager_->Configure(types, reason);
 }
 
 syncer::UserShare* ProfileSyncService::GetUserShare() const {
@@ -1773,16 +1995,14 @@ syncer::UserShare* ProfileSyncService::GetUserShare() const {
 }
 
 syncer::sessions::SyncSessionSnapshot
-    ProfileSyncService::GetLastSessionSnapshot() const {
-  if (backend_.get() && backend_initialized_) {
+ProfileSyncService::GetLastSessionSnapshot() const {
+  if (backend_)
     return backend_->GetLastSessionSnapshot();
-  }
-  NOTREACHED();
   return syncer::sessions::SyncSessionSnapshot();
 }
 
 bool ProfileSyncService::HasUnsyncedItems() const {
-  if (backend_.get() && backend_initialized_) {
+  if (HasSyncingBackend() && backend_initialized_) {
     return backend_->HasUnsyncedItems();
   }
   NOTREACHED();
@@ -1790,7 +2010,7 @@ bool ProfileSyncService::HasUnsyncedItems() const {
 }
 
 browser_sync::BackendMigrator*
-    ProfileSyncService::GetBackendMigratorForTest() {
+ProfileSyncService::GetBackendMigratorForTest() {
   return migrator_.get();
 }
 
@@ -1810,9 +2030,8 @@ base::Value* ProfileSyncService::GetTypeStatusMap() const {
     return result.release();
   }
 
-  FailedDataTypesHandler::TypeErrorMap error_map =
-      failed_data_types_handler_.GetAllErrors();
-
+  DataTypeStatusTable::TypeErrorMap error_map =
+      data_type_status_table_.GetAllErrors();
   ModelTypeSet active_types;
   ModelTypeSet passive_types;
   ModelSafeRoutingInfo routing_info;
@@ -1849,10 +2068,27 @@ base::Value* ProfileSyncService::GetTypeStatusMap() const {
     if (error_map.find(type) != error_map.end()) {
       const syncer::SyncError &error = error_map.find(type)->second;
       DCHECK(error.IsSet());
-      std::string error_text = "Error: " + error.location().ToString() +
-          ", " + error.message();
-      type_status->SetString("status", "error");
-      type_status->SetString("value", error_text);
+      switch (error.GetSeverity()) {
+        case syncer::SyncError::SYNC_ERROR_SEVERITY_ERROR: {
+            std::string error_text = "Error: " + error.location().ToString() +
+                ", " + error.GetMessagePrefix() + error.message();
+            type_status->SetString("status", "error");
+            type_status->SetString("value", error_text);
+          }
+          break;
+        case syncer::SyncError::SYNC_ERROR_SEVERITY_INFO:
+          type_status->SetString("status", "disabled");
+          type_status->SetString("value", error.message());
+          break;
+        default:
+          NOTREACHED() << "Unexpected error severity.";
+          break;
+      }
+    } else if (syncer::IsProxyType(type) && passive_types.Has(type)) {
+      // Show a proxy type in "ok" state unless it is disabled by user.
+      DCHECK(!throttled_types.Has(type));
+      type_status->SetString("status", "ok");
+      type_status->SetString("value", "Passive");
     } else if (throttled_types.Has(type) && passive_types.Has(type)) {
       type_status->SetString("status", "warning");
       type_status->SetString("value", "Passive, Throttled");
@@ -1862,6 +2098,9 @@ base::Value* ProfileSyncService::GetTypeStatusMap() const {
     } else if (throttled_types.Has(type)) {
       type_status->SetString("status", "warning");
       type_status->SetString("value", "Throttled");
+    } else if (GetRegisteredNonBlockingDataTypes().Has(type)) {
+      type_status->SetString("status", "ok");
+      type_status->SetString("value", "Non-Blocking");
     } else if (active_types.Has(type)) {
       type_status->SetString("status", "ok");
       type_status->SetString("value", "Active: " +
@@ -1882,17 +2121,6 @@ base::Value* ProfileSyncService::GetTypeStatusMap() const {
   return result.release();
 }
 
-void ProfileSyncService::ActivateDataType(
-    syncer::ModelType type, syncer::ModelSafeGroup group,
-    ChangeProcessor* change_processor) {
-  if (!backend_) {
-    NOTREACHED();
-    return;
-  }
-  DCHECK(backend_initialized_);
-  backend_->ActivateDataType(type, group, change_processor);
-}
-
 void ProfileSyncService::DeactivateDataType(syncer::ModelType type) {
   if (!backend_)
     return;
@@ -1903,7 +2131,7 @@ void ProfileSyncService::ConsumeCachedPassphraseIfPossible() {
   // If no cached passphrase, or sync backend hasn't started up yet, just exit.
   // If the backend isn't running yet, OnBackendInitialized() will call this
   // method again after the backend starts up.
-  if (cached_passphrase_.empty() || !sync_initialized())
+  if (cached_passphrase_.empty() || !backend_initialized())
     return;
 
   // Backend is up and running, so we can consume the cached passphrase.
@@ -1931,11 +2159,7 @@ void ProfileSyncService::RequestAccessToken() {
     return;
   request_access_token_retry_timer_.Stop();
   OAuth2TokenService::ScopeSet oauth2_scopes;
-  if (profile_->IsManaged()) {
-    oauth2_scopes.insert(GaiaConstants::kChromeSyncManagedOAuth2Scope);
-  } else {
-    oauth2_scopes.insert(GaiaConstants::kChromeSyncOAuth2Scope);
-  }
+  oauth2_scopes.insert(signin_->GetSyncScopeToUse());
 
   // Invalidate previous token, otherwise token service will return the same
   // token again.
@@ -1957,7 +2181,7 @@ void ProfileSyncService::RequestAccessToken() {
 void ProfileSyncService::SetEncryptionPassphrase(const std::string& passphrase,
                                                  PassphraseType type) {
   // This should only be called when the backend has been initialized.
-  DCHECK(sync_initialized());
+  DCHECK(backend_initialized());
   DCHECK(!(type == IMPLICIT && IsUsingSecondaryPassphrase())) <<
       "Data is already encrypted using an explicit passphrase";
   DCHECK(!(type == EXPLICIT &&
@@ -1990,11 +2214,22 @@ bool ProfileSyncService::SetDecryptionPassphrase(
   }
 }
 
+bool ProfileSyncService::EncryptEverythingAllowed() const {
+  return encrypt_everything_allowed_;
+}
+
+void ProfileSyncService::SetEncryptEverythingAllowed(bool allowed) {
+  DCHECK(allowed || !backend_initialized() || !EncryptEverythingEnabled());
+  encrypt_everything_allowed_ = allowed;
+}
+
 void ProfileSyncService::EnableEncryptEverything() {
-  // Tests override sync_initialized() to always return true, so we
+  DCHECK(EncryptEverythingAllowed());
+
+  // Tests override backend_initialized() to always return true, so we
   // must check that instead of |backend_initialized_|.
   // TODO(akalin): Fix the above. :/
-  DCHECK(sync_initialized());
+  DCHECK(backend_initialized());
   // TODO(atwilson): Persist the encryption_pending_ flag to address the various
   // problems around cancelling encryption in the background (crbug.com/119649).
   if (!encrypt_everything_)
@@ -2021,7 +2256,6 @@ syncer::ModelTypeSet ProfileSyncService::GetEncryptedDataTypes() const {
 }
 
 void ProfileSyncService::OnSyncManagedPrefChange(bool is_sync_managed) {
-  NotifyObservers();
   if (is_sync_managed) {
     DisableForUser();
   } else {
@@ -2030,7 +2264,8 @@ void ProfileSyncService::OnSyncManagedPrefChange(bool is_sync_managed) {
   }
 }
 
-void ProfileSyncService::GoogleSigninSucceeded(const std::string& username,
+void ProfileSyncService::GoogleSigninSucceeded(const std::string& account_id,
+                                               const std::string& username,
                                                const std::string& password) {
   if (!sync_prefs_.IsStartSuppressed() && !password.empty()) {
     cached_passphrase_ = password;
@@ -2042,15 +2277,21 @@ void ProfileSyncService::GoogleSigninSucceeded(const std::string& username,
 #if defined(OS_CHROMEOS)
   RefreshSpareBootstrapToken(password);
 #endif
-  if (!sync_initialized() || GetAuthError().state() != AuthError::NONE) {
+  if (!backend_initialized() || GetAuthError().state() != AuthError::NONE) {
     // Track the fact that we're still waiting for auth to complete.
     is_auth_in_progress_ = true;
   }
 }
 
-void ProfileSyncService::GoogleSignedOut(const std::string& username) {
+void ProfileSyncService::GoogleSignedOut(const std::string& account_id,
+                                         const std::string& username) {
   sync_disabled_by_admin_ = false;
   DisableForUser();
+
+  if (browser_sync::BackupRollbackController::IsBackupEnabled()) {
+    need_backup_ = true;
+    backup_finished_ = false;
+  }
 }
 
 void ProfileSyncService::AddObserver(
@@ -2066,18 +2307,152 @@ void ProfileSyncService::RemoveObserver(
 void ProfileSyncService::AddProtocolEventObserver(
     browser_sync::ProtocolEventObserver* observer) {
   protocol_event_observers_.AddObserver(observer);
-  if (backend_) {
-    backend_->SetForwardProtocolEvents(
-        protocol_event_observers_.might_have_observers());
+  if (HasSyncingBackend()) {
+    backend_->RequestBufferedProtocolEventsAndEnableForwarding();
   }
 }
 
 void ProfileSyncService::RemoveProtocolEventObserver(
     browser_sync::ProtocolEventObserver* observer) {
   protocol_event_observers_.RemoveObserver(observer);
-  if (backend_) {
-    backend_->SetForwardProtocolEvents(
-        protocol_event_observers_.might_have_observers());
+  if (HasSyncingBackend() &&
+      !protocol_event_observers_.might_have_observers()) {
+    backend_->DisableProtocolEventForwarding();
+  }
+}
+
+void ProfileSyncService::AddTypeDebugInfoObserver(
+    syncer::TypeDebugInfoObserver* type_debug_info_observer) {
+  type_debug_info_observers_.AddObserver(type_debug_info_observer);
+  if (type_debug_info_observers_.might_have_observers() &&
+      backend_initialized_) {
+    backend_->EnableDirectoryTypeDebugInfoForwarding();
+  }
+}
+
+void ProfileSyncService::RemoveTypeDebugInfoObserver(
+    syncer::TypeDebugInfoObserver* type_debug_info_observer) {
+  type_debug_info_observers_.RemoveObserver(type_debug_info_observer);
+  if (!type_debug_info_observers_.might_have_observers() &&
+      backend_initialized_) {
+    backend_->DisableDirectoryTypeDebugInfoForwarding();
+  }
+}
+
+void ProfileSyncService::AddPreferenceProvider(
+    SyncTypePreferenceProvider* provider) {
+  DCHECK(!HasPreferenceProvider(provider))
+      << "Providers may only be added once!";
+  preference_providers_.insert(provider);
+}
+
+void ProfileSyncService::RemovePreferenceProvider(
+    SyncTypePreferenceProvider* provider) {
+  DCHECK(HasPreferenceProvider(provider))
+      << "Only providers that have been added before can be removed!";
+  preference_providers_.erase(provider);
+}
+
+bool ProfileSyncService::HasPreferenceProvider(
+    SyncTypePreferenceProvider* provider) const {
+  return preference_providers_.count(provider) > 0;
+}
+
+namespace {
+
+class GetAllNodesRequestHelper
+    : public base::RefCountedThreadSafe<GetAllNodesRequestHelper> {
+ public:
+  GetAllNodesRequestHelper(
+      syncer::ModelTypeSet requested_types,
+      const base::Callback<void(scoped_ptr<base::ListValue>)>& callback);
+
+  void OnReceivedNodesForTypes(
+      const std::vector<syncer::ModelType>& types,
+      ScopedVector<base::ListValue> scoped_node_lists);
+
+ private:
+  friend class base::RefCountedThreadSafe<GetAllNodesRequestHelper>;
+  virtual ~GetAllNodesRequestHelper();
+
+  scoped_ptr<base::ListValue> result_accumulator_;
+
+  syncer::ModelTypeSet awaiting_types_;
+  base::Callback<void(scoped_ptr<base::ListValue>)> callback_;
+};
+
+GetAllNodesRequestHelper::GetAllNodesRequestHelper(
+    syncer::ModelTypeSet requested_types,
+    const base::Callback<void(scoped_ptr<base::ListValue>)>& callback)
+    : result_accumulator_(new base::ListValue()),
+      awaiting_types_(requested_types),
+      callback_(callback) {}
+
+GetAllNodesRequestHelper::~GetAllNodesRequestHelper() {
+  if (!awaiting_types_.Empty()) {
+    DLOG(WARNING)
+        << "GetAllNodesRequest deleted before request was fulfilled.  "
+        << "Missing types are: " << ModelTypeSetToString(awaiting_types_);
+  }
+}
+
+// Called when the set of nodes for a type or set of types has been returned.
+//
+// The nodes for several types can be returned at the same time by specifying
+// their types in the |types| array, and putting their results at the
+// correspnding indices in the |scoped_node_lists|.
+void GetAllNodesRequestHelper::OnReceivedNodesForTypes(
+    const std::vector<syncer::ModelType>& types,
+    ScopedVector<base::ListValue> scoped_node_lists) {
+  DCHECK_EQ(types.size(), scoped_node_lists.size());
+
+  // Take unsafe ownership of the node list.
+  std::vector<base::ListValue*> node_lists;
+  scoped_node_lists.release(&node_lists);
+
+  for (size_t i = 0; i < node_lists.size() && i < types.size(); ++i) {
+    const ModelType type = types[i];
+    base::ListValue* node_list = node_lists[i];
+
+    // Add these results to our list.
+    scoped_ptr<base::DictionaryValue> type_dict(new base::DictionaryValue());
+    type_dict->SetString("type", ModelTypeToString(type));
+    type_dict->Set("nodes", node_list);
+    result_accumulator_->Append(type_dict.release());
+
+    // Remember that this part of the request is satisfied.
+    awaiting_types_.Remove(type);
+  }
+
+  if (awaiting_types_.Empty()) {
+    callback_.Run(result_accumulator_.Pass());
+    callback_.Reset();
+  }
+}
+
+}  // namespace
+
+void ProfileSyncService::GetAllNodes(
+    const base::Callback<void(scoped_ptr<base::ListValue>)>& callback) {
+  ModelTypeSet directory_types = GetRegisteredDirectoryDataTypes();
+  directory_types.PutAll(syncer::ControlTypes());
+  scoped_refptr<GetAllNodesRequestHelper> helper =
+      new GetAllNodesRequestHelper(directory_types, callback);
+
+  if (!backend_initialized_) {
+    // If there's no backend available to fulfill the request, handle it here.
+    ScopedVector<base::ListValue> empty_results;
+    std::vector<ModelType> type_vector;
+    for (ModelTypeSet::Iterator it = directory_types.First();
+         it.Good(); it.Inc()) {
+      type_vector.push_back(it.Get());
+      empty_results.push_back(new base::ListValue());
+    }
+    helper->OnReceivedNodesForTypes(type_vector, empty_results.Pass());
+  } else {
+    backend_->GetAllNodesForTypes(
+        directory_types,
+        base::Bind(&GetAllNodesRequestHelper::OnReceivedNodesForTypes, helper));
   }
 }
 
@@ -2105,25 +2480,12 @@ bool ProfileSyncService::IsManaged() const {
   return sync_prefs_.IsManaged() || sync_disabled_by_admin_;
 }
 
-bool ProfileSyncService::ShouldPushChanges() {
-  // True only after all bootstrapping has succeeded: the sync backend
-  // is initialized, all enabled data types are consistent with one
-  // another, and no unrecoverable error has transpired.
-  if (HasUnrecoverableError())
-    return false;
-
-  if (!data_type_manager_)
-    return false;
-
-  return data_type_manager_->state() == DataTypeManager::CONFIGURED;
-}
-
 void ProfileSyncService::StopAndSuppress() {
   sync_prefs_.SetStartSuppressed(true);
-  if (backend_) {
+  if (HasSyncingBackend()) {
     backend_->UnregisterInvalidationIds();
   }
-  ShutdownImpl(browser_sync::SyncBackendHost::STOP_AND_CLAIM_THREAD);
+  ShutdownImpl(syncer::STOP_SYNC);
 }
 
 bool ProfileSyncService::IsStartSuppressed() const {
@@ -2139,10 +2501,7 @@ SigninManagerBase* ProfileSyncService::signin() const {
 void ProfileSyncService::UnsuppressAndStart() {
   DCHECK(profile_);
   sync_prefs_.SetStartSuppressed(false);
-  // Set username in SigninManager, as SigninManager::OnGetUserInfoSuccess
-  // is never called for some clients.
-  if (signin_.get() &&
-      signin_->GetOriginal()->GetAuthenticatedUsername().empty()) {
+  if (signin_.get() && !signin_->GetOriginal()->IsAuthenticated()) {
     signin_->GetOriginal()->SetAuthenticatedUsername(
         profile_->GetPrefs()->GetString(prefs::kGoogleServicesUsername));
   }
@@ -2171,9 +2530,21 @@ void ProfileSyncService::ReconfigureDatatypeManager() {
   }
 }
 
-const FailedDataTypesHandler& ProfileSyncService::failed_data_types_handler()
+syncer::ModelTypeSet ProfileSyncService::GetDataTypesFromPreferenceProviders()
     const {
-  return failed_data_types_handler_;
+  syncer::ModelTypeSet types;
+  for (std::set<SyncTypePreferenceProvider*>::const_iterator it =
+           preference_providers_.begin();
+       it != preference_providers_.end();
+       ++it) {
+    types.PutAll((*it)->GetPreferredDataTypes());
+  }
+  return types;
+}
+
+const DataTypeStatusTable& ProfileSyncService::data_type_status_table()
+    const {
+  return data_type_status_table_;
 }
 
 void ProfileSyncService::OnInternalUnrecoverableError(
@@ -2186,6 +2557,21 @@ void ProfileSyncService::OnInternalUnrecoverableError(
   OnUnrecoverableErrorImpl(from_here, message, delete_sync_database);
 }
 
+syncer::SyncManagerFactory::MANAGER_TYPE
+ProfileSyncService::GetManagerType() const {
+  switch (backend_mode_) {
+    case SYNC:
+      return syncer::SyncManagerFactory::NORMAL;
+    case BACKUP:
+      return syncer::SyncManagerFactory::BACKUP;
+    case ROLLBACK:
+      return syncer::SyncManagerFactory::ROLLBACK;
+    case IDLE:
+      NOTREACHED();
+  }
+  return syncer::SyncManagerFactory::NORMAL;
+}
+
 bool ProfileSyncService::IsRetryingAccessTokenFetchForTest() const {
   return request_access_token_retry_timer_.IsRunning();
 }
@@ -2202,6 +2588,10 @@ syncer::SyncableService* ProfileSyncService::GetSessionsSyncableService() {
   return sessions_sync_manager_.get();
 }
 
+syncer::SyncableService* ProfileSyncService::GetDeviceInfoSyncableService() {
+  return device_info_sync_service_.get();
+}
+
 ProfileSyncService::SyncTokenStatus::SyncTokenStatus()
     : connection_status(syncer::CONNECTION_NOT_ATTEMPTED),
       last_get_token_error(GoogleServiceAuthError::AuthErrorNone()) {}
@@ -2224,3 +2614,158 @@ void ProfileSyncService::OverrideNetworkResourcesForTest(
     scoped_ptr<syncer::NetworkResources> network_resources) {
   network_resources_ = network_resources.Pass();
 }
+
+bool ProfileSyncService::HasSyncingBackend() const {
+  return backend_mode_ != SYNC ? false : backend_ != NULL;
+}
+
+void ProfileSyncService::UpdateFirstSyncTimePref() {
+  if (signin_->GetEffectiveUsername().empty()) {
+    // Clear if user's not signed in and rollback is done.
+    if (backend_mode_ != ROLLBACK)
+      sync_prefs_.ClearFirstSyncTime();
+  } else if (sync_prefs_.GetFirstSyncTime().is_null() &&
+      backend_mode_ == SYNC) {
+    // Set if not set before and it's syncing now.
+    sync_prefs_.SetFirstSyncTime(base::Time::Now());
+  }
+}
+
+void ProfileSyncService::ClearBrowsingDataSinceFirstSync() {
+  base::Time first_sync_time = sync_prefs_.GetFirstSyncTime();
+  if (first_sync_time.is_null())
+    return;
+
+  clear_browsing_data_.Run(browsing_data_remover_observer_,
+                           profile_,
+                           first_sync_time,
+                           base::Time::Now());
+}
+
+void ProfileSyncService::SetBrowsingDataRemoverObserverForTesting(
+    BrowsingDataRemover::Observer* observer) {
+  browsing_data_remover_observer_ = observer;
+}
+
+void ProfileSyncService::SetClearingBrowseringDataForTesting(
+    base::Callback<void(BrowsingDataRemover::Observer* observer,
+                        Profile*,
+                        base::Time,
+                        base::Time)> c) {
+  clear_browsing_data_ = c;
+}
+
+GURL ProfileSyncService::GetSyncServiceURL(
+    const base::CommandLine& command_line) {
+  // By default, dev, canary, and unbranded Chromium users will go to the
+  // development servers. Development servers have more features than standard
+  // sync servers. Users with officially-branded Chrome stable and beta builds
+  // will go to the standard sync servers.
+  GURL result(kDevServerUrl);
+
+  chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
+  if (channel == chrome::VersionInfo::CHANNEL_STABLE ||
+      channel == chrome::VersionInfo::CHANNEL_BETA) {
+    result = GURL(kSyncServerUrl);
+  }
+
+  // Override the sync server URL from the command-line, if sync server
+  // command-line argument exists.
+  if (command_line.HasSwitch(switches::kSyncServiceURL)) {
+    std::string value(command_line.GetSwitchValueASCII(
+        switches::kSyncServiceURL));
+    if (!value.empty()) {
+      GURL custom_sync_url(value);
+      if (custom_sync_url.is_valid()) {
+        result = custom_sync_url;
+      } else {
+        LOG(WARNING) << "The following sync URL specified at the command-line "
+                     << "is invalid: " << value;
+      }
+    }
+  }
+  return result;
+}
+
+void ProfileSyncService::CheckSyncBackupIfNeeded() {
+  DCHECK_EQ(backend_mode_, SYNC);
+
+#if defined(ENABLE_PRE_SYNC_BACKUP)
+  const base::Time last_synced_time = sync_prefs_.GetLastSyncedTime();
+  // Check backup once a day.
+  if (!last_backup_time_ &&
+      (last_synced_time.is_null() ||
+          base::Time::Now() - last_synced_time >=
+              base::TimeDelta::FromDays(1))) {
+    // If sync thread is set, need to serialize check on sync thread after
+    // closing backup DB.
+    if (sync_thread_) {
+      sync_thread_->message_loop_proxy()->PostTask(
+          FROM_HERE,
+          base::Bind(syncer::CheckSyncDbLastModifiedTime,
+                     profile_->GetPath().Append(kSyncBackupDataFolderName),
+                     base::MessageLoopProxy::current(),
+                     base::Bind(&ProfileSyncService::CheckSyncBackupCallback,
+                                weak_factory_.GetWeakPtr())));
+    } else {
+      content::BrowserThread::PostTask(
+          content::BrowserThread::FILE, FROM_HERE,
+          base::Bind(syncer::CheckSyncDbLastModifiedTime,
+                     profile_->GetPath().Append(kSyncBackupDataFolderName),
+                     base::MessageLoopProxy::current(),
+                     base::Bind(&ProfileSyncService::CheckSyncBackupCallback,
+                                weak_factory_.GetWeakPtr())));
+    }
+  }
+#endif
+}
+
+void ProfileSyncService::CheckSyncBackupCallback(base::Time backup_time) {
+  last_backup_time_.reset(new base::Time(backup_time));
+
+  DCHECK(device_info_sync_service_);
+  device_info_sync_service_->UpdateLocalDeviceBackupTime(*last_backup_time_);
+}
+
+void ProfileSyncService::TryStartSyncAfterBackup() {
+  startup_controller_.Reset(GetRegisteredDataTypes());
+  startup_controller_.TryStart();
+}
+
+void ProfileSyncService::CleanUpBackup() {
+  sync_prefs_.ClearFirstSyncTime();
+  profile_->GetIOTaskRunner()->PostTask(
+      FROM_HERE,
+      base::Bind(base::IgnoreResult(base::DeleteFile),
+                 profile_->GetPath().Append(kSyncBackupDataFolderName),
+                 true));
+}
+
+bool ProfileSyncService::NeedBackup() const {
+  return need_backup_;
+}
+
+base::Time ProfileSyncService::GetDeviceBackupTimeForTesting() const {
+  return device_info_sync_service_->GetLocalDeviceBackupTime();
+}
+
+void ProfileSyncService::FlushDirectory() const {
+  // backend_initialized_ implies backend_ isn't NULL and the manager exists.
+  // If sync is not initialized yet, we fail silently.
+  if (backend_initialized_)
+    backend_->FlushDirectory();
+}
+
+base::FilePath ProfileSyncService::GetDirectoryPathForTest() const {
+  return directory_path_;
+}
+
+base::MessageLoop* ProfileSyncService::GetSyncLoopForTest() const {
+  if (sync_thread_) {
+    return sync_thread_->message_loop();
+  } else if (backend_) {
+    return backend_->GetSyncLoopForTesting();
+  } else {
+    return NULL;
+  }
+}