blob: 47350212eb6e718244019488f28d3367a75cb55a [file] [log] [blame]
// 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(
const 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";
}
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(const base::Closure& 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());
}
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