blob: e19cd414ce23bf4106dfdb826bf66e9043989aa8 [file] [log] [blame]
// Copyright 2013 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 "components/sync/driver/glue/sync_engine_impl.h"
#include <utility>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/task_runner_util.h"
#include "build/build_config.h"
#include "components/invalidation/impl/invalidation_switches.h"
#include "components/invalidation/public/invalidation_service.h"
#include "components/invalidation/public/topic_invalidation_map.h"
#include "components/sync/base/bind_to_task_runner.h"
#include "components/sync/base/invalidation_helper.h"
#include "components/sync/base/sync_prefs.h"
#include "components/sync/driver/glue/sync_engine_backend.h"
#include "components/sync/driver/sync_driver_switches.h"
#include "components/sync/engine/data_type_activation_response.h"
#include "components/sync/engine/engine_components_factory.h"
#include "components/sync/engine/engine_components_factory_impl.h"
#include "components/sync/engine/events/protocol_event.h"
#include "components/sync/engine/net/http_bridge.h"
#include "components/sync/engine/sync_backend_registrar.h"
#include "components/sync/engine/sync_engine_host.h"
#include "components/sync/engine/sync_manager_factory.h"
#include "components/sync/engine/sync_string_conversions.h"
#include "components/sync/invalidations/fcm_handler.h"
#include "components/sync/invalidations/switches.h"
#include "components/sync/invalidations/sync_invalidations_service.h"
namespace syncer {
SyncEngineImpl::SyncEngineImpl(
const std::string& name,
invalidation::InvalidationService* invalidator,
SyncInvalidationsService* sync_invalidations_service,
const base::WeakPtr<SyncPrefs>& sync_prefs,
const base::FilePath& sync_data_folder)
: sync_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
{base::MayBlock(), base::TaskPriority::USER_VISIBLE,
base::TaskShutdownBehavior::BLOCK_SHUTDOWN})),
name_(name),
sync_prefs_(sync_prefs),
invalidator_(invalidator),
sync_invalidations_service_(sync_invalidations_service),
#if defined(OS_ANDROID)
sessions_invalidation_enabled_(false) {
#else
sessions_invalidation_enabled_(true) {
#endif
backend_ = base::MakeRefCounted<SyncEngineBackend>(
name_, sync_data_folder, weak_ptr_factory_.GetWeakPtr());
}
SyncEngineImpl::~SyncEngineImpl() {
DCHECK(!backend_ && !host_) << "Must call Shutdown before destructor.";
DCHECK(!registrar_);
}
void SyncEngineImpl::Initialize(InitParams params) {
DCHECK(params.host);
DCHECK(params.registrar);
host_ = params.host;
registrar_ = params.registrar.get();
sync_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&SyncEngineBackend::DoInitialize, backend_,
std::move(params)));
// If the new invalidations system (SyncInvalidationsService) is fully
// enabled, then the SyncService doesn't need to communicate with the old
// InvalidationService anymore.
if (invalidator_ &&
base::FeatureList::IsEnabled(switches::kSyncSendInterestedDataTypes) &&
base::FeatureList::IsEnabled(switches::kUseSyncInvalidations) &&
base::FeatureList::IsEnabled(
switches::kUseSyncInvalidationsForWalletAndOffer)) {
DCHECK(!invalidation_handler_registered_);
invalidator_->RegisterInvalidationHandler(this);
bool success = invalidator_->UpdateInterestedTopics(this, /*topics=*/{});
DCHECK(success);
invalidator_->UnregisterInvalidationHandler(this);
invalidator_ = nullptr;
}
}
bool SyncEngineImpl::IsInitialized() const {
return initialized_;
}
void SyncEngineImpl::TriggerRefresh(const ModelTypeSet& types) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
sync_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&SyncEngineBackend::DoRefreshTypes, backend_, types));
}
void SyncEngineImpl::UpdateCredentials(const SyncCredentials& credentials) {
sync_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&SyncEngineBackend::DoUpdateCredentials,
backend_, credentials));
}
void SyncEngineImpl::InvalidateCredentials() {
sync_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&SyncEngineBackend::DoInvalidateCredentials, backend_));
}
void SyncEngineImpl::StartConfiguration() {
sync_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&SyncEngineBackend::DoStartConfiguration, backend_));
}
void SyncEngineImpl::StartSyncingWithServer() {
DVLOG(1) << name_ << ": SyncEngineImpl::StartSyncingWithServer called.";
base::Time last_poll_time = sync_prefs_->GetLastPollTime();
// If there's no known last poll time (e.g. on initial start-up), we treat
// this as if a poll just happened.
if (last_poll_time.is_null()) {
last_poll_time = base::Time::Now();
sync_prefs_->SetLastPollTime(last_poll_time);
}
sync_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&SyncEngineBackend::DoStartSyncing, backend_,
last_poll_time));
}
void SyncEngineImpl::SetEncryptionPassphrase(const std::string& passphrase) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
sync_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&SyncEngineBackend::DoSetEncryptionPassphrase,
backend_, passphrase));
}
void SyncEngineImpl::SetDecryptionPassphrase(const std::string& passphrase) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
sync_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&SyncEngineBackend::DoSetDecryptionPassphrase,
backend_, passphrase));
}
void SyncEngineImpl::AddTrustedVaultDecryptionKeys(
const std::vector<std::vector<uint8_t>>& keys,
base::OnceClosure done_cb) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
sync_task_runner_->PostTaskAndReply(
FROM_HERE,
base::BindOnce(&SyncEngineBackend::DoAddTrustedVaultDecryptionKeys,
backend_, keys),
std::move(done_cb));
}
void SyncEngineImpl::StopSyncingForShutdown() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Stop getting messages from the sync thread.
weak_ptr_factory_.InvalidateWeakPtrs();
// Immediately stop sending messages to the host.
host_ = nullptr;
registrar_->RequestWorkerStopOnUIThread();
backend_->ShutdownOnUIThread();
}
void SyncEngineImpl::Shutdown(ShutdownReason reason) {
// StopSyncingForShutdown() (which nulls out |host_|) should be
// called first.
DCHECK(!host_);
if (invalidation_handler_registered_) {
if (reason != BROWSER_SHUTDOWN) {
bool success = invalidator_->UpdateInterestedTopics(this, /*topics=*/{});
DCHECK(success);
}
invalidator_->UnregisterInvalidationHandler(this);
invalidator_ = nullptr;
}
if (sync_invalidations_service_) {
// It's safe to call RemoveListener even if AddListener wasn't called
// before.
sync_invalidations_service_->RemoveListener(this);
sync_invalidations_service_ = nullptr;
}
last_enabled_types_.Clear();
invalidation_handler_registered_ = false;
model_type_connector_.reset();
// Shut down and destroy SyncManager. SyncManager holds a pointer to
// |registrar_| so its destruction must be sequenced before the destruction of
// |registrar_|.
sync_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&SyncEngineBackend::DoShutdown, backend_, reason));
// Ensure that |backend_| destroyed inside Sync sequence, not inside current
// one.
sync_task_runner_->ReleaseSoon(FROM_HERE, std::move(backend_));
DCHECK(!backend_);
registrar_ = nullptr;
}
void SyncEngineImpl::ConfigureDataTypes(ConfigureParams params) {
sync_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&SyncEngineBackend::DoPurgeDisabledTypes,
backend_, params.to_purge));
sync_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&SyncEngineBackend::DoConfigureSyncer, backend_,
std::move(params)));
}
void SyncEngineImpl::EnableEncryptEverything() {
sync_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&SyncEngineBackend::DoEnableEncryptEverything, backend_));
}
void SyncEngineImpl::ActivateNonBlockingDataType(
ModelType type,
std::unique_ptr<DataTypeActivationResponse> activation_response) {
registrar_->RegisterNonBlockingType(type);
if (activation_response->model_type_state.initial_sync_done())
registrar_->AddRestoredNonBlockingType(type);
model_type_connector_->ConnectNonBlockingType(type,
std::move(activation_response));
}
void SyncEngineImpl::DeactivateNonBlockingDataType(ModelType type) {
model_type_connector_->DisconnectNonBlockingType(type);
}
void SyncEngineImpl::ActivateProxyDataType(ModelType type) {
model_type_connector_->ConnectProxyType(type);
}
void SyncEngineImpl::DeactivateProxyDataType(ModelType type) {
model_type_connector_->DisconnectProxyType(type);
}
const SyncEngineImpl::Status& SyncEngineImpl::GetDetailedStatus() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(IsInitialized());
return cached_status_;
}
void SyncEngineImpl::HasUnsyncedItemsForTest(
base::OnceCallback<void(bool)> cb) const {
DCHECK(IsInitialized());
base::PostTaskAndReplyWithResult(
sync_task_runner_.get(), FROM_HERE,
base::BindOnce(&SyncEngineBackend::HasUnsyncedItemsForTest, backend_),
std::move(cb));
}
void SyncEngineImpl::GetModelSafeRoutingInfo(ModelSafeRoutingInfo* out) const {
if (IsInitialized()) {
registrar_->GetModelSafeRoutingInfo(out);
} else {
NOTREACHED();
}
}
void SyncEngineImpl::RequestBufferedProtocolEventsAndEnableForwarding() {
sync_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&SyncEngineBackend::SendBufferedProtocolEventsAndEnableForwarding,
backend_));
}
void SyncEngineImpl::DisableProtocolEventForwarding() {
sync_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&SyncEngineBackend::DisableProtocolEventForwarding,
backend_));
}
void SyncEngineImpl::FinishConfigureDataTypesOnFrontendLoop(
const ModelTypeSet enabled_types,
const ModelTypeSet succeeded_configuration_types,
const ModelTypeSet failed_configuration_types,
base::OnceCallback<void(ModelTypeSet, ModelTypeSet)> ready_task) {
last_enabled_types_ = enabled_types;
SendInterestedTopicsToInvalidator();
if (!ready_task.is_null()) {
std::move(ready_task)
.Run(succeeded_configuration_types, failed_configuration_types);
}
}
void SyncEngineImpl::HandleInitializationSuccessOnFrontendLoop(
ModelTypeSet initial_types,
const WeakHandle<JsBackend> js_backend,
const WeakHandle<DataTypeDebugInfoListener> debug_info_listener,
std::unique_ptr<ModelTypeConnector> model_type_connector,
const std::string& birthday,
const std::string& bag_of_chips) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
model_type_connector_ = std::move(model_type_connector);
initialized_ = true;
if (invalidator_) {
invalidator_->RegisterInvalidationHandler(this);
invalidation_handler_registered_ = true;
// Fake a state change to initialize the SyncManager's cached invalidator
// state.
// TODO(crbug.com/1132868): Do this for the new invalidations as well.
OnInvalidatorStateChange(invalidator_->GetInvalidatorState());
}
if (sync_invalidations_service_) {
sync_invalidations_service_->AddListener(this);
}
host_->OnEngineInitialized(initial_types, js_backend, debug_info_listener,
birthday, bag_of_chips, /*success=*/true);
}
void SyncEngineImpl::HandleInitializationFailureOnFrontendLoop() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
host_->OnEngineInitialized(ModelTypeSet(), WeakHandle<JsBackend>(),
WeakHandle<DataTypeDebugInfoListener>(),
/*birthday=*/"", /*bag_of_chips=*/"",
/*success=*/false);
}
void SyncEngineImpl::HandleSyncCycleCompletedOnFrontendLoop(
const SyncCycleSnapshot& snapshot) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Process any changes to the datatypes we're syncing.
// TODO(sync): add support for removing types.
if (IsInitialized()) {
host_->OnSyncCycleCompleted(snapshot);
}
}
void SyncEngineImpl::HandleActionableErrorEventOnFrontendLoop(
const SyncProtocolError& sync_error) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
host_->OnActionableError(sync_error);
}
void SyncEngineImpl::HandleMigrationRequestedOnFrontendLoop(
ModelTypeSet types) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
host_->OnMigrationNeededForTypes(types);
}
void SyncEngineImpl::OnInvalidatorStateChange(InvalidatorState state) {
sync_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&SyncEngineBackend::DoOnInvalidatorStateChange,
backend_, state));
}
void SyncEngineImpl::OnIncomingInvalidation(
const TopicInvalidationMap& invalidation_map) {
sync_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&SyncEngineBackend::DoOnIncomingInvalidation,
backend_, invalidation_map));
}
std::string SyncEngineImpl::GetOwnerName() const {
return "SyncEngineImpl";
}
void SyncEngineImpl::HandleConnectionStatusChangeOnFrontendLoop(
ConnectionStatus status) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DVLOG(1) << "Connection status changed: " << ConnectionStatusToString(status);
host_->OnConnectionStatusChange(status);
}
void SyncEngineImpl::HandleProtocolEventOnFrontendLoop(
std::unique_ptr<ProtocolEvent> event) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
host_->OnProtocolEvent(*event);
}
void SyncEngineImpl::UpdateInvalidationVersions(
const std::map<ModelType, int64_t>& invalidation_versions) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
sync_prefs_->UpdateInvalidationVersions(invalidation_versions);
}
void SyncEngineImpl::HandleSyncStatusChanged(const SyncStatus& status) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
const bool backed_off_types_changed =
(status.backed_off_types != cached_status_.backed_off_types);
cached_status_ = status;
if (backed_off_types_changed) {
host_->OnBackedOffTypesChanged();
}
}
void SyncEngineImpl::OnCookieJarChanged(bool account_mismatch,
bool empty_jar,
base::OnceClosure callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
sync_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&SyncEngineBackend::DoOnCookieJarChanged, backend_,
account_mismatch, empty_jar, std::move(callback)));
}
void SyncEngineImpl::SetInvalidationsForSessionsEnabled(bool enabled) {
sessions_invalidation_enabled_ = enabled;
SendInterestedTopicsToInvalidator();
}
void SyncEngineImpl::GetNigoriNodeForDebugging(AllNodesCallback callback) {
DCHECK(backend_);
sync_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&SyncEngineBackend::GetNigoriNodeForDebugging, backend_,
BindToCurrentSequence(std::move(callback))));
}
void SyncEngineImpl::OnInvalidatorClientIdChange(const std::string& client_id) {
sync_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&SyncEngineBackend::DoOnInvalidatorClientIdChange,
backend_, client_id));
}
void SyncEngineImpl::OnInvalidationReceived(const std::string& payload) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// TODO(crbug.com/1082122): check that sync engine is fully initialized.
sync_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&SyncEngineBackend::DoOnInvalidationReceived,
backend_, payload));
}
void SyncEngineImpl::OnCookieJarChangedDoneOnFrontendLoop(
base::OnceClosure callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::move(callback).Run();
}
void SyncEngineImpl::SendInterestedTopicsToInvalidator() {
if (!invalidator_) {
return;
}
// No need to register invalidations for CommitOnlyTypes().
ModelTypeSet invalidation_enabled_types(
Difference(last_enabled_types_, CommitOnlyTypes()));
if (!sessions_invalidation_enabled_) {
invalidation_enabled_types.Remove(syncer::SESSIONS);
}
// switches::kUseSyncInvalidations means that the new invalidations system is
// used for all data types except Wallet and Offer, so only keep these types.
if (base::FeatureList::IsEnabled(switches::kSyncSendInterestedDataTypes) &&
base::FeatureList::IsEnabled(switches::kUseSyncInvalidations)) {
invalidation_enabled_types.RetainAll(
{AUTOFILL_WALLET_DATA, AUTOFILL_WALLET_OFFER});
}
bool success = invalidator_->UpdateInterestedTopics(
this, ModelTypeSetToTopicSet(invalidation_enabled_types));
DCHECK(success);
}
} // namespace syncer