text: // Copyright 2014 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "chrome/browser/sync_file_system/drive_backend/sync_worker.h"
  
  #include <utility>
  #include <vector>
  
  #include "base/bind.h"
  #include "base/callback_helpers.h"
  #include "chrome/browser/extensions/extension_service.h"
  #include "chrome/browser/sync_file_system/drive_backend/callback_helper.h"
  #include "chrome/browser/sync_file_system/drive_backend/conflict_resolver.h"
  #include "chrome/browser/sync_file_system/drive_backend/drive_backend_constants.h"
  #include "chrome/browser/sync_file_system/drive_backend/list_changes_task.h"
  #include "chrome/browser/sync_file_system/drive_backend/local_to_remote_syncer.h"
  #include "chrome/browser/sync_file_system/drive_backend/metadata_database.h"
  #include "chrome/browser/sync_file_system/drive_backend/register_app_task.h"
  #include "chrome/browser/sync_file_system/drive_backend/remote_change_processor_on_worker.h"
  #include "chrome/browser/sync_file_system/drive_backend/remote_to_local_syncer.h"
  #include "chrome/browser/sync_file_system/drive_backend/sync_engine_context.h"
  #include "chrome/browser/sync_file_system/drive_backend/sync_engine_initializer.h"
  #include "chrome/browser/sync_file_system/drive_backend/sync_task.h"
  #include "chrome/browser/sync_file_system/drive_backend/uninstall_app_task.h"
  #include "chrome/browser/sync_file_system/logger.h"
  #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
  #include "components/drive/service/drive_service_interface.h"
  #include "extensions/browser/extension_registry.h"
  #include "storage/common/file_system/file_system_util.h"
  
  namespace sync_file_system {
  
  class RemoteChangeProcessor;
  
  namespace drive_backend {
  
  namespace {
  
  void InvokeIdleCallback(const base::Closure& idle_callback,
                          SyncStatusCallback callback) {
    idle_callback.Run();
    std::move(callback).Run(SYNC_STATUS_OK);
  }
  
  }  // namespace
  
  SyncWorker::SyncWorker(
      const base::FilePath& base_dir,
      const base::WeakPtr<extensions::ExtensionServiceInterface>&
          extension_service,
      extensions::ExtensionRegistry* extension_registry,
      leveldb::Env* env_override)
      : base_dir_(base_dir),
        env_override_(env_override),
        service_state_(REMOTE_SERVICE_TEMPORARY_UNAVAILABLE),
        should_check_conflict_(true),
        should_check_remote_change_(true),
        listing_remote_changes_(false),
        sync_enabled_(false),
        extension_service_(extension_service),
        extension_registry_(extension_registry) {
    sequence_checker_.DetachFromSequence();
    DCHECK(base_dir_.IsAbsolute());
  }
  
  SyncWorker::~SyncWorker() {
    observers_.Clear();
  }
  
  void SyncWorker::Initialize(std::unique_ptr<SyncEngineContext> context) {
    DCHECK(sequence_checker_.CalledOnValidSequence());
    DCHECK(!task_manager_);
  
    context_ = std::move(context);
  
    task_manager_.reset(new SyncTaskManager(weak_ptr_factory_.GetWeakPtr(),
                                            0 /* maximum_background_task */,
                                            context_->GetWorkerTaskRunner()));
    task_manager_->Initialize(SYNC_STATUS_OK);
  
    PostInitializeTask();
  }
  
  void SyncWorker::RegisterOrigin(const GURL& origin,
                                  SyncStatusCallback callback) {
    DCHECK(sequence_checker_.CalledOnValidSequence());
  
    if (!GetMetadataDatabase())
      PostInitializeTask();
  
    std::unique_ptr<RegisterAppTask> task(
        new RegisterAppTask(context_.get(), origin.host()));
    if (task->CanFinishImmediately()) {
      std::move(callback).Run(SYNC_STATUS_OK);
      return;
    }
  
    task_manager_->ScheduleSyncTask(FROM_HERE, std::move(task),
                                    SyncTaskManager::PRIORITY_HIGH,
                                    std::move(callback));
  }
  
  void SyncWorker::EnableOrigin(const GURL& origin, SyncStatusCallback callback) {
    DCHECK(sequence_checker_.CalledOnValidSequence());
  
    task_manager_->ScheduleTask(
        FROM_HERE,
        base::Bind(&SyncWorker::DoEnableApp, weak_ptr_factory_.GetWeakPtr(),
                   origin.host()),
        SyncTaskManager::PRIORITY_HIGH, std::move(callback));
  }
  
  void SyncWorker::DisableOrigin(const GURL& origin,
                                 SyncStatusCallback callback) {
    DCHECK(sequence_checker_.CalledOnValidSequence());
  
    task_manager_->ScheduleTask(
        FROM_HERE,
        base::Bind(&SyncWorker::DoDisableApp, weak_ptr_factory_.GetWeakPtr(),
                   origin.host()),
        SyncTaskManager::PRIORITY_HIGH, std::move(callback));
  }
  
  void SyncWorker::UninstallOrigin(const GURL& origin,
                                   RemoteFileSyncService::UninstallFlag flag,
                                   SyncStatusCallback callback) {
    DCHECK(sequence_checker_.CalledOnValidSequence());
  
    task_manager_->ScheduleSyncTask(
        FROM_HERE,
        std::make_unique<UninstallAppTask>(context_.get(), origin.host(), flag),
        SyncTaskManager::PRIORITY_HIGH, std::move(callback));
  }
  
  void SyncWorker::ProcessRemoteChange(SyncFileCallback callback) {
    DCHECK(sequence_checker_.CalledOnValidSequence());
  
    RemoteToLocalSyncer* syncer = new RemoteToLocalSyncer(context_.get());
    task_manager_->ScheduleSyncTask(
        FROM_HERE, std::unique_ptr<SyncTask>(syncer),
        SyncTaskManager::PRIORITY_MED,
        base::BindOnce(&SyncWorker::DidProcessRemoteChange,
                       weak_ptr_factory_.GetWeakPtr(), syncer,
                       std::move(callback)));
  }
  
  void SyncWorker::SetRemoteChangeProcessor(
      RemoteChangeProcessorOnWorker* remote_change_processor_on_worker) {
    DCHECK(sequence_checker_.CalledOnValidSequence());
  
    context_->SetRemoteChangeProcessor(remote_change_processor_on_worker);
  }
  
  RemoteServiceState SyncWorker::GetCurrentState() const {
    DCHECK(sequence_checker_.CalledOnValidSequence());
  
    if (!sync_enabled_)
      return REMOTE_SERVICE_DISABLED;
    return service_state_;
  }
  
  void SyncWorker::GetOriginStatusMap(
      RemoteFileSyncService::StatusMapCallback callback) {
    DCHECK(sequence_checker_.CalledOnValidSequence());
  
    if (!GetMetadataDatabase())
      return;
  
    std::vector<std::string> app_ids;
    GetMetadataDatabase()->GetRegisteredAppIDs(&app_ids);
  
    std::unique_ptr<RemoteFileSyncService::OriginStatusMap> status_map(
        new RemoteFileSyncService::OriginStatusMap);
    for (std::vector<std::string>::const_iterator itr = app_ids.begin();
         itr != app_ids.end(); ++itr) {
      const std::string& app_id = *itr;
      GURL origin = extensions::Extension::GetBaseURLFromExtensionId(app_id);
      (*status_map)[origin] =
          GetMetadataDatabase()->IsAppEnabled(app_id) ? "Enabled" : "Disabled";
    }
  
    std::move(callback).Run(std::move(status_map));
  }
  
  std::unique_ptr<base::ListValue> SyncWorker::DumpFiles(const GURL& origin) {
    DCHECK(sequence_checker_.CalledOnValidSequence());
  
    if (!GetMetadataDatabase())
      return std::unique_ptr<base::ListValue>();
    return GetMetadataDatabase()->DumpFiles(origin.host());
  }
  
  std::unique_ptr<base::ListValue> SyncWorker::DumpDatabase() {
    DCHECK(sequence_checker_.CalledOnValidSequence());
  
    if (!GetMetadataDatabase())
      return std::unique_ptr<base::ListValue>();
    return GetMetadataDatabase()->DumpDatabase();
  }
  
  void SyncWorker::SetSyncEnabled(bool enabled) {
    DCHECK(sequence_checker_.CalledOnValidSequence());
  
    if (sync_enabled_ == enabled)
      return;
  
    RemoteServiceState old_state = GetCurrentState();
    sync_enabled_ = enabled;
    if (old_state == GetCurrentState())
      return;
  
    for (auto& observer : observers_) {
      observer.UpdateServiceState(
          GetCurrentState(), enabled ? "Sync is enabled" : "Sync is disabled");
    }
  }
  
  void SyncWorker::PromoteDemotedChanges(base::OnceClosure callback) {
    DCHECK(sequence_checker_.CalledOnValidSequence());
  
    MetadataDatabase* metadata_db = GetMetadataDatabase();
    if (metadata_db && metadata_db->HasDemotedDirtyTracker()) {
      metadata_db->PromoteDemotedTrackers();
      for (auto& observer : observers_)
        observer.OnPendingFileListUpdated(metadata_db->CountDirtyTracker());
    }
    std::move(callback).Run();
  }
  
  void SyncWorker::ApplyLocalChange(const FileChange& local_change,
                                    const base::FilePath& local_path,
                                    const SyncFileMetadata& local_metadata,
                                    const storage::FileSystemURL& url,
                                    SyncStatusCallback callback) {
    DCHECK(sequence_checker_.CalledOnValidSequence());
  
    LocalToRemoteSyncer* syncer = new LocalToRemoteSyncer(
        context_.get(), local_metadata, local_change, local_path, url);
    task_manager_->ScheduleSyncTask(
        FROM_HERE, std::unique_ptr<SyncTask>(syncer),
        SyncTaskManager::PRIORITY_MED,
        base::BindOnce(&SyncWorker::DidApplyLocalChange,
                       weak_ptr_factory_.GetWeakPtr(), syncer,
                       std::move(callback)));
  }
  
  void SyncWorker::MaybeScheduleNextTask() {
    DCHECK(sequence_checker_.CalledOnValidSequence());
  
    if (GetCurrentState() == REMOTE_SERVICE_DISABLED)
      return;
  
    // TODO(tzik): Notify observer of OnRemoteChangeQueueUpdated.
    // TODO(tzik): Add an interface to get the number of dirty trackers to
    // MetadataDatabase.
  
    if (MaybeStartFetchChanges())
      return;
  
    if (!call_on_idle_callback_.is_null()) {
      base::Closure callback = call_on_idle_callback_;
      call_on_idle_callback_.Reset();
      callback.Run();
    }
  }
  
  void SyncWorker::NotifyLastOperationStatus(
      SyncStatusCode status,
      bool used_network) {
    DCHECK(sequence_checker_.CalledOnValidSequence());
  
    UpdateServiceStateFromSyncStatusCode(status, used_network);
  
    if (GetMetadataDatabase()) {
      for (auto& observer : observers_) {
        observer.OnPendingFileListUpdated(
            GetMetadataDatabase()->CountDirtyTracker());
      }
    }
  }
  
  void SyncWorker::RecordTaskLog(std::unique_ptr<TaskLogger::TaskLog> task_log) {
    DCHECK(sequence_checker_.CalledOnValidSequence());
  
    context_->GetUITaskRunner()->PostTask(
        FROM_HERE,
        base::BindOnce(&TaskLogger::RecordLog, context_->GetTaskLogger(),
                       std::move(task_log)));
  }
  
  void SyncWorker::ActivateService(RemoteServiceState service_state,
                                   const std::string& description) {
    DCHECK(sequence_checker_.CalledOnValidSequence());
    UpdateServiceState(service_state, description);
    if (!GetMetadataDatabase()) {
      PostInitializeTask();
      return;
    }
  
    should_check_remote_change_ = true;
    MaybeScheduleNextTask();
  }
  
  void SyncWorker::DeactivateService(const std::string& description) {
    DCHECK(sequence_checker_.CalledOnValidSequence());
    UpdateServiceState(REMOTE_SERVICE_TEMPORARY_UNAVAILABLE, description);
  }
  
  void SyncWorker::DetachFromSequence() {
    task_manager_->DetachFromSequence();
    context_->DetachFromSequence();
    sequence_checker_.DetachFromSequence();
  }
  
  void SyncWorker::AddObserver(Observer* observer) {
    observers_.AddObserver(observer);
  }
  
  void SyncWorker::DoDisableApp(const std::string& app_id,
                                SyncStatusCallback callback) {
    DCHECK(sequence_checker_.CalledOnValidSequence());
  
    if (!GetMetadataDatabase()) {
      std::move(callback).Run(SYNC_STATUS_OK);
      return;
    }
  
    SyncStatusCode status = GetMetadataDatabase()->DisableApp(app_id);
    std::move(callback).Run(status);
  }
  
  void SyncWorker::DoEnableApp(const std::string& app_id,
                               SyncStatusCallback callback) {
    DCHECK(sequence_checker_.CalledOnValidSequence());
  
    if (!GetMetadataDatabase()) {
      std::move(callback).Run(SYNC_STATUS_OK);
      return;
    }
  
    SyncStatusCode status = GetMetadataDatabase()->EnableApp(app_id);
    std::move(callback).Run(status);
  }
  
  void SyncWorker::PostInitializeTask() {
    DCHECK(sequence_checker_.CalledOnValidSequence());
    DCHECK(!GetMetadataDatabase());
  
    // This initializer task may not run if MetadataDatabase in context_ is
    // already initialized when it runs.
    SyncEngineInitializer* initializer =
        new SyncEngineInitializer(context_.get(),
                                  base_dir_.Append(kDatabaseName),
                                  env_override_);
    task_manager_->ScheduleSyncTask(
        FROM_HERE, std::unique_ptr<SyncTask>(initializer),
        SyncTaskManager::PRIORITY_HIGH,
        base::Bind(&SyncWorker::DidInitialize, weak_ptr_factory_.GetWeakPtr(),
                   initializer));
  }
  
  void SyncWorker::DidInitialize(SyncEngineInitializer* initializer,
                                 SyncStatusCode status) {
    DCHECK(sequence_checker_.CalledOnValidSequence());
  
    if (status == SYNC_STATUS_ACCESS_FORBIDDEN) {
      UpdateServiceState(REMOTE_SERVICE_ACCESS_FORBIDDEN, "Access forbidden");
      return;
    }
    if (status != SYNC_STATUS_OK) {
      UpdateServiceState(REMOTE_SERVICE_TEMPORARY_UNAVAILABLE,
                         "Could not initialize remote service");
      return;
    }
  
    std::unique_ptr<MetadataDatabase> metadata_database =
        initializer->PassMetadataDatabase();
    if (metadata_database) {
      context_->SetMetadataDatabase(std::move(metadata_database));
      return;
    }
  
    UpdateServiceState(REMOTE_SERVICE_OK, std::string());
    UpdateRegisteredApps();
  }
  
  void SyncWorker::UpdateRegisteredApps() {
    MetadataDatabase* metadata_db = GetMetadataDatabase();
    DCHECK(sequence_checker_.CalledOnValidSequence());
    DCHECK(metadata_db);
  
    std::unique_ptr<std::vector<std::string>> app_ids(
        new std::vector<std::string>);
    metadata_db->GetRegisteredAppIDs(app_ids.get());
  
    AppStatusMap* app_status = new AppStatusMap;
    base::Closure callback =
        base::Bind(&SyncWorker::DidQueryAppStatus,
                   weak_ptr_factory_.GetWeakPtr(),
                   base::Owned(app_status));
  
    context_->GetUITaskRunner()->PostTask(
        FROM_HERE,
        base::BindOnce(&SyncWorker::QueryAppStatusOnUIThread, extension_service_,
                       // This is protected by checking the extension_service_
                       // weak pointer, since the underlying ExtensionService
                       // also relies on the ExtensionRegistry.
                       base::Unretained(extension_registry_),
                       base::Owned(app_ids.release()), app_status,
                       RelayCallbackToTaskRunner(context_->GetWorkerTaskRunner(),
                                                 FROM_HERE, callback)));
  }
  
  void SyncWorker::QueryAppStatusOnUIThread(
      const base::WeakPtr<extensions::ExtensionServiceInterface>&
          extension_service_ptr,
      extensions::ExtensionRegistry* extension_registry,
      const std::vector<std::string>* app_ids,
      AppStatusMap* status,
      const base::Closure& callback) {
    extensions::ExtensionServiceInterface* extension_service =
        extension_service_ptr.get();
    if (!extension_service) {
      callback.Run();
      return;
    }
  
    for (auto itr = app_ids->begin(); itr != app_ids->end(); ++itr) {
      const std::string& app_id = *itr;
      if (!extension_registry->GetInstalledExtension(app_id))
        (*status)[app_id] = APP_STATUS_UNINSTALLED;
      else if (!extension_service->IsExtensionEnabled(app_id))
        (*status)[app_id] = APP_STATUS_DISABLED;
      else
        (*status)[app_id] = APP_STATUS_ENABLED;
    }
  
    callback.Run();
  }
  
  void SyncWorker::DidQueryAppStatus(const AppStatusMap* app_status) {
    DCHECK(sequence_checker_.CalledOnValidSequence());
  
    MetadataDatabase* metadata_db = GetMetadataDatabase();
    DCHECK(metadata_db);
  
    // Update the status of every origin using status from ExtensionService.
    for (auto itr = app_status->begin(); itr != app_status->end(); ++itr) {
      const std::string& app_id = itr->first;
      GURL origin = extensions::Extension::GetBaseURLFromExtensionId(app_id);
  
      if (itr->second == APP_STATUS_UNINSTALLED) {
        // Extension has been uninstalled.
        // (At this stage we can't know if it was unpacked extension or not,
        // so just purge the remote folder.)
        UninstallOrigin(origin, RemoteFileSyncService::UNINSTALL_AND_PURGE_REMOTE,
                        base::DoNothing());
        continue;
      }
  
      FileTracker tracker;
      if (!metadata_db->FindAppRootTracker(app_id, &tracker)) {
        // App will register itself on first run.
        continue;
      }
  
      DCHECK(itr->second == APP_STATUS_ENABLED ||
             itr->second == APP_STATUS_DISABLED);
      bool is_app_enabled = (itr->second == APP_STATUS_ENABLED);
      bool is_app_root_tracker_enabled =
          (tracker.tracker_kind() == TRACKER_KIND_APP_ROOT);
      if (is_app_enabled && !is_app_root_tracker_enabled)
        EnableOrigin(origin, base::DoNothing());
      else if (!is_app_enabled && is_app_root_tracker_enabled)
        DisableOrigin(origin, base::DoNothing());
    }
  }
  
  void SyncWorker::DidProcessRemoteChange(RemoteToLocalSyncer* syncer,
                                          SyncFileCallback callback,
                                          SyncStatusCode status) {
    DCHECK(sequence_checker_.CalledOnValidSequence());
  
    if (syncer->is_sync_root_deletion()) {
      MetadataDatabase::ClearDatabase(context_->PassMetadataDatabase());
      PostInitializeTask();
      std::move(callback).Run(status, syncer->url());
      return;
    }
  
    if (status == SYNC_STATUS_OK) {
      if (syncer->sync_action() != SYNC_ACTION_NONE &&
          syncer->url().is_valid()) {
        for (auto& observer : observers_) {
          observer.OnFileStatusChanged(
              syncer->url(), syncer->file_type(), SYNC_FILE_STATUS_SYNCED,
              syncer->sync_action(), SYNC_DIRECTION_REMOTE_TO_LOCAL);
        }
      }
  
      if (syncer->sync_action() == SYNC_ACTION_DELETED &&
          syncer->url().is_valid() &&
          storage::VirtualPath::IsRootPath(syncer->url().path())) {
        RegisterOrigin(syncer->url().origin().GetURL(), base::DoNothing());
      }
      should_check_conflict_ = true;
    }
    std::move(callback).Run(status, syncer->url());
  }
  
  void SyncWorker::DidApplyLocalChange(LocalToRemoteSyncer* syncer,
                                       SyncStatusCallback callback,
                                       SyncStatusCode status) {
    DCHECK(sequence_checker_.CalledOnValidSequence());
  
    if ((status == SYNC_STATUS_OK || status == SYNC_STATUS_RETRY) &&
        syncer->url().is_valid() &&
        syncer->sync_action() != SYNC_ACTION_NONE) {
      storage::FileSystemURL updated_url = syncer->url();
      if (!syncer->target_path().empty()) {
        updated_url = CreateSyncableFileSystemURL(syncer->url().origin().GetURL(),
                                                  syncer->target_path());
      }
      for (auto& observer : observers_) {
        observer.OnFileStatusChanged(
            updated_url, syncer->file_type(), SYNC_FILE_STATUS_SYNCED,
            syncer->sync_action(), SYNC_DIRECTION_LOCAL_TO_REMOTE);
      }
    }
  
    if (status == SYNC_STATUS_UNKNOWN_ORIGIN && syncer->url().is_valid())
      RegisterOrigin(syncer->url().origin().GetURL(), base::DoNothing());
  
    if (syncer->needs_remote_change_listing() &&
        !listing_remote_changes_) {
      task_manager_->ScheduleSyncTask(
          FROM_HERE,
          std::unique_ptr<SyncTask>(new ListChangesTask(context_.get())),
          SyncTaskManager::PRIORITY_HIGH,
          base::Bind(&SyncWorker::DidFetchChanges,
                     weak_ptr_factory_.GetWeakPtr()));
      should_check_remote_change_ = false;
      listing_remote_changes_ = true;
      time_to_check_changes_ =
          base::TimeTicks::Now() +
          base::TimeDelta::FromSeconds(kListChangesRetryDelaySeconds);
    }
  
    if (status == SYNC_STATUS_OK)
      should_check_conflict_ = true;
  
    std::move(callback).Run(status);
  }
  
  bool SyncWorker::MaybeStartFetchChanges() {
    DCHECK(sequence_checker_.CalledOnValidSequence());
  
    if (GetCurrentState() == REMOTE_SERVICE_DISABLED)
      return false;
  
    if (!GetMetadataDatabase())
      return false;
  
    if (listing_remote_changes_)
      return false;
  
    base::TimeTicks now = base::TimeTicks::Now();
    if (!should_check_remote_change_ && now < time_to_check_changes_) {
      if (!GetMetadataDatabase()->HasDirtyTracker() &&
          should_check_conflict_) {
        should_check_conflict_ = false;
        return task_manager_->ScheduleSyncTaskIfIdle(
            FROM_HERE,
            std::unique_ptr<SyncTask>(new ConflictResolver(context_.get())),
            base::Bind(&SyncWorker::DidResolveConflict,
                       weak_ptr_factory_.GetWeakPtr()));
      }
      return false;
    }
  
    if (task_manager_->ScheduleSyncTaskIfIdle(
            FROM_HERE,
            std::unique_ptr<SyncTask>(new ListChangesTask(context_.get())),
            base::Bind(&SyncWorker::DidFetchChanges,
                       weak_ptr_factory_.GetWeakPtr()))) {
      should_check_remote_change_ = false;
      listing_remote_changes_ = true;
      time_to_check_changes_ =
          now + base::TimeDelta::FromSeconds(kListChangesRetryDelaySeconds);
      return true;
    }
    return false;
  }
  
  void SyncWorker::DidResolveConflict(SyncStatusCode status) {
    DCHECK(sequence_checker_.CalledOnValidSequence());
  
    if (status == SYNC_STATUS_OK || status == SYNC_STATUS_RETRY)
      should_check_conflict_ = true;
  }
  
  void SyncWorker::DidFetchChanges(SyncStatusCode status) {
    DCHECK(sequence_checker_.CalledOnValidSequence());
  
    if (status == SYNC_STATUS_OK)
      should_check_conflict_ = true;
    listing_remote_changes_ = false;
  }
  
  void SyncWorker::UpdateServiceStateFromSyncStatusCode(
      SyncStatusCode status,
      bool used_network) {
    switch (status) {
      case SYNC_STATUS_OK:
        if (used_network)
          UpdateServiceState(REMOTE_SERVICE_OK, std::string());
        break;
  
      // Authentication error.
      case SYNC_STATUS_AUTHENTICATION_FAILED:
        UpdateServiceState(REMOTE_SERVICE_AUTHENTICATION_REQUIRED,
                           "Authentication required");
        break;
  
      // OAuth token error.
      case SYNC_STATUS_ACCESS_FORBIDDEN:
        UpdateServiceState(REMOTE_SERVICE_ACCESS_FORBIDDEN,
                           "Access forbidden");
        break;
  
      // Errors which could make the service temporarily unavailable.
      case SYNC_STATUS_SERVICE_TEMPORARILY_UNAVAILABLE:
      case SYNC_STATUS_NETWORK_ERROR:
      case SYNC_STATUS_ABORT:
      case SYNC_STATUS_FAILED:
        UpdateServiceState(REMOTE_SERVICE_TEMPORARY_UNAVAILABLE,
                           "Network or temporary service error.");
        break;
  
      // Errors which would require manual user intervention to resolve.
      case SYNC_DATABASE_ERROR_CORRUPTION:
      case SYNC_DATABASE_ERROR_IO_ERROR:
      case SYNC_DATABASE_ERROR_FAILED:
        UpdateServiceState(REMOTE_SERVICE_DISABLED,
                           "Unrecoverable database error");
        break;
  
      default:
        // Other errors don't affect service state
        break;
    }
  }
  
  void SyncWorker::UpdateServiceState(RemoteServiceState state,
                                      const std::string& description) {
    DCHECK(sequence_checker_.CalledOnValidSequence());
  
    RemoteServiceState old_state = GetCurrentState();
    service_state_ = state;
  
    if (old_state == GetCurrentState())
      return;
  
    util::Log(logging::LOG_VERBOSE, FROM_HERE,
              "Service state changed: %d->%d: %s",
              old_state, GetCurrentState(), description.c_str());
  
    for (auto& observer : observers_)
      observer.UpdateServiceState(GetCurrentState(), description);
  }
  
  void SyncWorker::CallOnIdleForTesting(const base::Closure& callback) {
    if (task_manager_->ScheduleTaskIfIdle(
            FROM_HERE, base::Bind(&InvokeIdleCallback, callback),
            base::DoNothing()))
      return;
    call_on_idle_callback_ = base::Bind(
        &SyncWorker::CallOnIdleForTesting,
        weak_ptr_factory_.GetWeakPtr(),
        callback);
  }
  
  drive::DriveServiceInterface* SyncWorker::GetDriveService() {
    DCHECK(sequence_checker_.CalledOnValidSequence());
    return context_->GetDriveService();
  }
  
  drive::DriveUploaderInterface* SyncWorker::GetDriveUploader() {
    DCHECK(sequence_checker_.CalledOnValidSequence());
    return context_->GetDriveUploader();
  }
  
  MetadataDatabase* SyncWorker::GetMetadataDatabase() {
    DCHECK(sequence_checker_.CalledOnValidSequence());
    return context_->GetMetadataDatabase();
  }
  
  }  // namespace drive_backend
  }  // namespace sync_file_system
  