// Copyright (c) 2012 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 "build/build_config.h" #include "chrome/browser/sync/glue/sync_backend_host.h" #include #include #include "base/bind.h" #include "base/command_line.h" #include "base/compiler_specific.h" #include "base/file_path.h" #include "base/file_util.h" #include "base/location.h" #include "base/metrics/histogram.h" #include "base/threading/thread_restrictions.h" #include "base/timer.h" #include "base/tracked_objects.h" #include "base/utf_string_conversions.h" #include "build/build_config.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/signin/token_service.h" #include "chrome/browser/signin/token_service_factory.h" #include "chrome/browser/sync/glue/bridged_invalidator.h" #include "chrome/browser/sync/glue/change_processor.h" #include "chrome/browser/sync/glue/chrome_encryptor.h" #include "chrome/browser/sync/glue/chrome_sync_notification_bridge.h" #include "chrome/browser/sync/glue/device_info.h" #include "chrome/browser/sync/glue/sync_backend_registrar.h" #include "chrome/browser/sync/glue/synced_device_tracker.h" #include "chrome/browser/sync/invalidations/invalidator_storage.h" #include "chrome/browser/sync/sync_prefs.h" #include "chrome/common/chrome_notification_types.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/chrome_version_info.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/notification_service.h" #include "content/public/common/content_client.h" #include "google_apis/gaia/gaia_constants.h" #include "jingle/notifier/base/notification_method.h" #include "jingle/notifier/base/notifier_options.h" #include "net/base/host_port_pair.h" #include "net/url_request/url_request_context_getter.h" #include "sync/internal_api/public/base_transaction.h" #include "sync/internal_api/public/engine/model_safe_worker.h" #include "sync/internal_api/public/http_bridge.h" #include "sync/internal_api/public/internal_components_factory_impl.h" #include "sync/internal_api/public/read_transaction.h" #include "sync/internal_api/public/sync_manager_factory.h" #include "sync/internal_api/public/util/experiments.h" #include "sync/internal_api/public/util/sync_string_conversions.h" #include "sync/notifier/invalidator.h" #include "sync/protocol/encryption.pb.h" #include "sync/protocol/sync.pb.h" #include "sync/util/nigori.h" static const int kSaveChangesIntervalSeconds = 10; static const FilePath::CharType kSyncDataFolderName[] = FILE_PATH_LITERAL("Sync Data"); typedef TokenService::TokenAvailableDetails TokenAvailableDetails; typedef GoogleServiceAuthError AuthError; namespace browser_sync { using content::BrowserThread; using syncer::InternalComponentsFactory; using syncer::InternalComponentsFactoryImpl; using syncer::sessions::SyncSessionSnapshot; using syncer::SyncCredentials; // Helper macros to log with the syncer thread name; useful when there // are multiple syncers involved. #define SLOG(severity) LOG(severity) << name_ << ": " #define SDVLOG(verbose_level) DVLOG(verbose_level) << name_ << ": " class SyncBackendHost::Core : public base::RefCountedThreadSafe, public syncer::SyncEncryptionHandler::Observer, public syncer::SyncManager::Observer, public syncer::InvalidationHandler { public: Core(const std::string& name, const FilePath& sync_data_folder_path, const base::WeakPtr& backend); // SyncManager::Observer implementation. The Core just acts like an air // traffic controller here, forwarding incoming messages to appropriate // landing threads. virtual void OnSyncCycleCompleted( const syncer::sessions::SyncSessionSnapshot& snapshot) OVERRIDE; virtual void OnInitializationComplete( const syncer::WeakHandle& js_backend, const syncer::WeakHandle& debug_info_listener, bool success, syncer::ModelTypeSet restored_types) OVERRIDE; virtual void OnConnectionStatusChange( syncer::ConnectionStatus status) OVERRIDE; virtual void OnStopSyncingPermanently() OVERRIDE; virtual void OnUpdatedToken(const std::string& token) OVERRIDE; virtual void OnActionableError( const syncer::SyncProtocolError& sync_error) OVERRIDE; // SyncEncryptionHandler::Observer implementation. virtual void OnPassphraseRequired( syncer::PassphraseRequiredReason reason, const sync_pb::EncryptedData& pending_keys) OVERRIDE; virtual void OnPassphraseAccepted() OVERRIDE; virtual void OnBootstrapTokenUpdated( const std::string& bootstrap_token, syncer::BootstrapTokenType type) OVERRIDE; virtual void OnEncryptedTypesChanged( syncer::ModelTypeSet encrypted_types, bool encrypt_everything) OVERRIDE; virtual void OnEncryptionComplete() OVERRIDE; virtual void OnCryptographerStateChanged( syncer::Cryptographer* cryptographer) OVERRIDE; virtual void OnPassphraseTypeChanged(syncer::PassphraseType type, base::Time passphrase_time) OVERRIDE; // syncer::InvalidationHandler implementation. virtual void OnInvalidatorStateChange( syncer::InvalidatorState state) OVERRIDE; virtual void OnIncomingInvalidation( const syncer::ObjectIdInvalidationMap& invalidation_map, syncer::IncomingInvalidationSource source) OVERRIDE; // Note: // // The Do* methods are the various entry points from our // SyncBackendHost. They are all called on the sync thread to // actually perform synchronous (and potentially blocking) syncapi // operations. // // Called to perform initialization of the syncapi on behalf of // SyncBackendHost::Initialize. void DoInitialize(const DoInitializeOptions& options); // Called to perform credential update on behalf of // SyncBackendHost::UpdateCredentials. void DoUpdateCredentials(const syncer::SyncCredentials& credentials); // Called to update the given registered ids on behalf of // SyncBackendHost::UpdateRegisteredInvalidationIds. void DoUpdateRegisteredInvalidationIds(const syncer::ObjectIdSet& ids); // Called to tell the syncapi to start syncing (generally after // initialization and authentication). void DoStartSyncing(const syncer::ModelSafeRoutingInfo& routing_info); // Called to set the passphrase for encryption. void DoSetEncryptionPassphrase(const std::string& passphrase, bool is_explicit); // Called to decrypt the pending keys. void DoSetDecryptionPassphrase(const std::string& passphrase); // Called to turn on encryption of all sync data as well as // reencrypt everything. void DoEnableEncryptEverything(); // Called at startup to download the control types. Will invoke // DoInitialProcessControlTypes on success, and OnControlTypesDownloadRetry // if an error occurred. void DoDownloadControlTypes(); // Invoked if we failed to download the necessary control types at startup. // Invokes SyncBackendHost::HandleControlTypesDownloadRetry. void OnControlTypesDownloadRetry(); // Called to perform tasks which require the control data to be downloaded. // This includes refreshing encryption, setting up the device info change // processor, etc. void DoInitialProcessControlTypes(); // Some parts of DoInitialProcessControlTypes() may be executed on a different // thread. This function asynchronously continues the work started in // DoInitialProcessControlTypes() once that other thread gets back to us. void DoFinishInitialProcessControlTypes(); // The shutdown order is a bit complicated: // 1) From |sync_thread_|, invoke the syncapi Shutdown call to do // a final SaveChanges, and close sqlite handles. // 2) Then, from |frontend_loop_|, halt the sync_thread_ (which is // a blocking call). This causes syncapi thread-exit handlers // to run and make use of cached pointers to various components // owned implicitly by us. // 3) Destroy this Core. That will delete syncapi components in a // safe order because the thread that was using them has exited // (in step 2). void DoStopSyncManagerForShutdown(const base::Closure& closure); void DoShutdown(bool stopping_sync); void DoDestroySyncManager(); // Configuration methods that must execute on sync loop. void DoConfigureSyncer( syncer::ConfigureReason reason, syncer::ModelTypeSet types_to_config, const syncer::ModelSafeRoutingInfo routing_info, const base::Callback& ready_task, const base::Closure& retry_callback); void DoFinishConfigureDataTypes( syncer::ModelTypeSet types_to_config, const base::Callback& ready_task); void DoRetryConfiguration( const base::Closure& retry_callback); // Set the base request context to use when making HTTP calls. // This method will add a reference to the context to persist it // on the IO thread. Must be removed from IO thread. syncer::SyncManager* sync_manager() { return sync_manager_.get(); } SyncedDeviceTracker* synced_device_tracker() { return synced_device_tracker_.get(); } // Delete the sync data folder to cleanup backend data. Happens the first // time sync is enabled for a user (to prevent accidentally reusing old // sync databases), as well as shutdown when you're no longer syncing. void DeleteSyncDataFolder(); private: friend class base::RefCountedThreadSafe; friend class SyncBackendHostForProfileSyncTest; virtual ~Core(); // Invoked when initialization of syncapi is complete and we can start // our timer. // This must be called from the thread on which SaveChanges is intended to // be run on; the host's |sync_thread_|. void StartSavingChanges(); // Invoked periodically to tell the syncapi to persist its state // by writing to disk. // This is called from the thread we were created on (which is the // SyncBackendHost |sync_thread_|), using a repeating timer that is kicked // off as soon as the SyncManager tells us it completed // initialization. void SaveChanges(); // Name used for debugging. const std::string name_; // Path of the folder that stores the sync data files. const FilePath sync_data_folder_path_; // Our parent SyncBackendHost. syncer::WeakHandle host_; // The loop where all the sync backend operations happen. // Non-NULL only between calls to DoInitialize() and DoShutdown(). MessageLoop* sync_loop_; // Our parent's registrar (not owned). Non-NULL only between // calls to DoInitialize() and DoShutdown(). SyncBackendRegistrar* registrar_; // Our parent's notification bridge (not owned). Non-NULL only // between calls to DoInitialize() and DoShutdown(). ChromeSyncNotificationBridge* chrome_sync_notification_bridge_; // The timer used to periodically call SaveChanges. scoped_ptr > save_changes_timer_; // Our encryptor, which uses Chrome's encryption functions. ChromeEncryptor encryptor_; // A special ChangeProcessor that tracks the DEVICE_INFO type for us. scoped_ptr synced_device_tracker_; // The top-level syncapi entry point. Lives on the sync thread. scoped_ptr sync_manager_; // Whether or not we registered with |sync_manager_| as an invalidation // handler. Necessary since we may end up trying to unregister before we // register in tests (in synchronous initialization mode). // // TODO(akalin): Fix this behavior (see http://crbug.com/140354). bool registered_as_invalidation_handler_; DISALLOW_COPY_AND_ASSIGN(Core); }; namespace { // Parses the given command line for notifier options. notifier::NotifierOptions ParseNotifierOptions( const CommandLine& command_line, const scoped_refptr& request_context_getter) { notifier::NotifierOptions notifier_options; notifier_options.request_context_getter = request_context_getter; if (command_line.HasSwitch(switches::kSyncNotificationHostPort)) { notifier_options.xmpp_host_port = net::HostPortPair::FromString( command_line.GetSwitchValueASCII( switches::kSyncNotificationHostPort)); DVLOG(1) << "Using " << notifier_options.xmpp_host_port.ToString() << " for test sync notification server."; } notifier_options.try_ssltcp_first = command_line.HasSwitch(switches::kSyncTrySsltcpFirstForXmpp); DVLOG_IF(1, notifier_options.try_ssltcp_first) << "Trying SSL/TCP port before XMPP port for notifications."; notifier_options.invalidate_xmpp_login = command_line.HasSwitch(switches::kSyncInvalidateXmppLogin); DVLOG_IF(1, notifier_options.invalidate_xmpp_login) << "Invalidating sync XMPP login."; notifier_options.allow_insecure_connection = command_line.HasSwitch(switches::kSyncAllowInsecureXmppConnection); DVLOG_IF(1, notifier_options.allow_insecure_connection) << "Allowing insecure XMPP connections."; if (command_line.HasSwitch(switches::kSyncNotificationMethod)) { const std::string notification_method_str( command_line.GetSwitchValueASCII(switches::kSyncNotificationMethod)); notifier_options.notification_method = notifier::StringToNotificationMethod(notification_method_str); } return notifier_options; } } // namespace SyncBackendHost::SyncBackendHost( const std::string& name, Profile* profile, const base::WeakPtr& sync_prefs, const base::WeakPtr& invalidator_storage) : weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), sync_thread_("Chrome_SyncThread"), frontend_loop_(MessageLoop::current()), profile_(profile), name_(name), core_(new Core(name, profile_->GetPath().Append(kSyncDataFolderName), weak_ptr_factory_.GetWeakPtr())), initialization_state_(NOT_ATTEMPTED), sync_prefs_(sync_prefs), invalidator_factory_( ParseNotifierOptions(*CommandLine::ForCurrentProcess(), profile_->GetRequestContext()), content::GetUserAgent(GURL()), invalidator_storage), frontend_(NULL), cached_passphrase_type_(syncer::IMPLICIT_PASSPHRASE) { } SyncBackendHost::SyncBackendHost(Profile* profile) : weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), sync_thread_("Chrome_SyncThread"), frontend_loop_(MessageLoop::current()), profile_(profile), name_("Unknown"), initialization_state_(NOT_ATTEMPTED), invalidator_factory_( ParseNotifierOptions(*CommandLine::ForCurrentProcess(), profile_->GetRequestContext()), content::GetUserAgent(GURL()), base::WeakPtr()), frontend_(NULL), cached_passphrase_type_(syncer::IMPLICIT_PASSPHRASE) { } SyncBackendHost::~SyncBackendHost() { DCHECK(!core_ && !frontend_) << "Must call Shutdown before destructor."; DCHECK(!chrome_sync_notification_bridge_.get()); DCHECK(!registrar_.get()); } namespace { scoped_ptr MakeHttpBridgeFactory( const scoped_refptr& getter) { chrome::VersionInfo version_info; return scoped_ptr( new syncer::HttpBridgeFactory( getter, DeviceInfo::MakeUserAgentForSyncApi(version_info))); } } // namespace void SyncBackendHost::Initialize( SyncFrontend* frontend, const syncer::WeakHandle& event_handler, const GURL& sync_service_url, const SyncCredentials& credentials, bool delete_sync_data_folder, syncer::SyncManagerFactory* sync_manager_factory, syncer::UnrecoverableErrorHandler* unrecoverable_error_handler, syncer::ReportUnrecoverableErrorFunction report_unrecoverable_error_function) { if (!sync_thread_.Start()) return; chrome_sync_notification_bridge_.reset( new ChromeSyncNotificationBridge( profile_, sync_thread_.message_loop_proxy())); frontend_ = frontend; DCHECK(frontend); registrar_.reset(new SyncBackendRegistrar(name_, profile_, sync_thread_.message_loop())); syncer::ModelSafeRoutingInfo routing_info; std::vector workers; registrar_->GetModelSafeRoutingInfo(&routing_info); registrar_->GetWorkers(&workers); InternalComponentsFactory::Switches factory_switches = { InternalComponentsFactory::ENCRYPTION_LEGACY, InternalComponentsFactory::BACKOFF_NORMAL }; CommandLine* cl = CommandLine::ForCurrentProcess(); if (cl->HasSwitch(switches::kSyncKeystoreEncryption)) { factory_switches.encryption_method = InternalComponentsFactoryImpl::ENCRYPTION_KEYSTORE; } if (cl->HasSwitch(switches::kSyncShortInitialRetryOverride)) { factory_switches.backoff_override = InternalComponentsFactoryImpl::BACKOFF_SHORT_INITIAL_RETRY_OVERRIDE; } initialization_state_ = CREATING_SYNC_MANAGER; InitCore(DoInitializeOptions( sync_thread_.message_loop(), registrar_.get(), routing_info, workers, &extensions_activity_monitor_, event_handler, sync_service_url, base::Bind(&MakeHttpBridgeFactory, make_scoped_refptr(profile_->GetRequestContext())), credentials, chrome_sync_notification_bridge_.get(), &invalidator_factory_, sync_manager_factory, delete_sync_data_folder, sync_prefs_->GetEncryptionBootstrapToken(), sync_prefs_->GetKeystoreEncryptionBootstrapToken(), new InternalComponentsFactoryImpl(factory_switches), unrecoverable_error_handler, report_unrecoverable_error_function)); } void SyncBackendHost::UpdateCredentials(const SyncCredentials& credentials) { DCHECK(sync_thread_.IsRunning()); sync_thread_.message_loop()->PostTask(FROM_HERE, base::Bind(&SyncBackendHost::Core::DoUpdateCredentials, core_.get(), credentials)); } void SyncBackendHost::UpdateRegisteredInvalidationIds( const syncer::ObjectIdSet& ids) { DCHECK_EQ(MessageLoop::current(), frontend_loop_); DCHECK(sync_thread_.IsRunning()); sync_thread_.message_loop()->PostTask(FROM_HERE, base::Bind(&SyncBackendHost::Core::DoUpdateRegisteredInvalidationIds, core_.get(), ids)); } void SyncBackendHost::StartSyncingWithServer() { SDVLOG(1) << "SyncBackendHost::StartSyncingWithServer called."; syncer::ModelSafeRoutingInfo routing_info; registrar_->GetModelSafeRoutingInfo(&routing_info); sync_thread_.message_loop()->PostTask(FROM_HERE, base::Bind(&SyncBackendHost::Core::DoStartSyncing, core_.get(), routing_info)); } void SyncBackendHost::SetEncryptionPassphrase(const std::string& passphrase, bool is_explicit) { DCHECK(sync_thread_.IsRunning()); if (!IsNigoriEnabled()) { NOTREACHED() << "SetEncryptionPassphrase must never be called when nigori" " is disabled."; return; } // We should never be called with an empty passphrase. DCHECK(!passphrase.empty()); // This should only be called by the frontend. DCHECK_EQ(MessageLoop::current(), frontend_loop_); // SetEncryptionPassphrase should never be called if we are currently // encrypted with an explicit passphrase. DCHECK(cached_passphrase_type_ == syncer::KEYSTORE_PASSPHRASE || cached_passphrase_type_ == syncer::IMPLICIT_PASSPHRASE); // Post an encryption task on the syncer thread. sync_thread_.message_loop()->PostTask(FROM_HERE, base::Bind(&SyncBackendHost::Core::DoSetEncryptionPassphrase, core_.get(), passphrase, is_explicit)); } bool SyncBackendHost::SetDecryptionPassphrase(const std::string& passphrase) { if (!IsNigoriEnabled()) { NOTREACHED() << "SetDecryptionPassphrase must never be called when nigori" " is disabled."; return false; } // We should never be called with an empty passphrase. DCHECK(!passphrase.empty()); // This should only be called by the frontend. DCHECK_EQ(MessageLoop::current(), frontend_loop_); // This should only be called when we have cached pending keys. DCHECK(cached_pending_keys_.has_blob()); // Check the passphrase that was provided against our local cache of the // cryptographer's pending keys. If this was unsuccessful, the UI layer can // immediately call OnPassphraseRequired without showing the user a spinner. if (!CheckPassphraseAgainstCachedPendingKeys(passphrase)) return false; // Post a decryption task on the syncer thread. sync_thread_.message_loop()->PostTask(FROM_HERE, base::Bind(&SyncBackendHost::Core::DoSetDecryptionPassphrase, core_.get(), passphrase)); // Since we were able to decrypt the cached pending keys with the passphrase // provided, we immediately alert the UI layer that the passphrase was // accepted. This will avoid the situation where a user enters a passphrase, // clicks OK, immediately reopens the advanced settings dialog, and gets an // unnecessary prompt for a passphrase. // Note: It is not guaranteed that the passphrase will be accepted by the // syncer thread, since we could receive a new nigori node while the task is // pending. This scenario is a valid race, and SetDecryptionPassphrase can // trigger a new OnPassphraseRequired if it needs to. NotifyPassphraseAccepted(); return true; } void SyncBackendHost::StopSyncManagerForShutdown( const base::Closure& closure) { DCHECK_GT(initialization_state_, NOT_ATTEMPTED); if (initialization_state_ == CREATING_SYNC_MANAGER) { // We post here to implicitly wait for the SyncManager to be created, // if needed. We have to wait, since we need to shutdown immediately, // and we need to tell the SyncManager so it can abort any activity // (net I/O, data application). DCHECK(sync_thread_.IsRunning()); sync_thread_.message_loop()->PostTask(FROM_HERE, base::Bind( &SyncBackendHost::Core::DoStopSyncManagerForShutdown, core_.get(), closure)); } else { core_->DoStopSyncManagerForShutdown(closure); } } void SyncBackendHost::StopSyncingForShutdown() { DCHECK_EQ(MessageLoop::current(), frontend_loop_); // Immediately stop sending messages to the frontend. frontend_ = NULL; // Thread shutdown should occur in the following order: // - Sync Thread // - UI Thread (stops some time after we return from this call). // // In order to achieve this, we first shutdown components from the UI thread // and send signals to abort components that may be busy on the sync thread. // The callback (OnSyncerShutdownComplete) will happen on the sync thread, // after which we'll shutdown components on the sync thread, and then be // able to stop the sync loop. if (sync_thread_.IsRunning()) { StopSyncManagerForShutdown( base::Bind(&SyncBackendRegistrar::OnSyncerShutdownComplete, base::Unretained(registrar_.get()))); // Before joining the sync_thread_, we wait for the UIModelWorker to // give us the green light that it is not depending on the frontend_loop_ // to process any more tasks. Stop() blocks until this termination // condition is true. base::Time stop_registrar_start_time = base::Time::Now(); if (registrar_.get()) registrar_->StopOnUIThread(); base::TimeDelta stop_registrar_time = base::Time::Now() - stop_registrar_start_time; UMA_HISTOGRAM_TIMES("Sync.Shutdown.StopRegistrarTime", stop_registrar_time); } else { // If the sync thread isn't running, then the syncer is effectively // stopped. Moreover, it implies that we never attempted initialization, // so the registrar won't need stopping either. DCHECK_EQ(initialization_state_, NOT_ATTEMPTED); DCHECK(!registrar_.get()); } } void SyncBackendHost::Shutdown(bool sync_disabled) { // StopSyncingForShutdown() (which nulls out |frontend_|) should be // called first. DCHECK(!frontend_); // TODO(tim): DCHECK(registrar_->StoppedOnUIThread()) would be nice. if (sync_thread_.IsRunning()) { sync_thread_.message_loop()->PostTask(FROM_HERE, base::Bind(&SyncBackendHost::Core::DoShutdown, core_.get(), sync_disabled)); if (chrome_sync_notification_bridge_.get()) chrome_sync_notification_bridge_->StopForShutdown(); } // Stop will return once the thread exits, which will be after DoShutdown // runs. DoShutdown needs to run from sync_thread_ because the sync backend // requires any thread that opened sqlite handles to relinquish them // personally. We need to join threads, because otherwise the main Chrome // thread (ui loop) can exit before DoShutdown finishes, at which point // virtually anything the sync backend does (or the post-back to // frontend_loop_ by our Core) will epically fail because the CRT won't be // initialized. // Since we are blocking the UI thread here, we need to turn ourselves in // with the ThreadRestriction police. For sentencing and how we plan to fix // this, see bug 19757. base::Time stop_thread_start_time = base::Time::Now(); { base::ThreadRestrictions::ScopedAllowIO allow_io; sync_thread_.Stop(); } base::TimeDelta stop_sync_thread_time = base::Time::Now() - stop_thread_start_time; UMA_HISTOGRAM_TIMES("Sync.Shutdown.StopSyncThreadTime", stop_sync_thread_time); registrar_.reset(); js_backend_.Reset(); chrome_sync_notification_bridge_.reset(); core_ = NULL; // Releases reference to core_. } void SyncBackendHost::ConfigureDataTypes( syncer::ConfigureReason reason, syncer::ModelTypeSet types_to_add, syncer::ModelTypeSet types_to_remove, const base::Callback& ready_task, const base::Callback& retry_callback) { // Only one configure is allowed at a time. This is guaranteed by our // callers. The SyncBackendHost requests one configure as the backend is // initializing and waits for it to complete. After initialization, all // configurations will pass through the DataTypeManager, which is careful to // never send a new configure request until the current request succeeds. DCHECK_EQ(initialization_state_, INITIALIZED); // The SyncBackendRegistrar's routing info will be updated by adding the // types_to_add to the list then removing types_to_remove. Any types which // are not in either of those sets will remain untouched. // // Types which were not in the list previously are not fully downloaded, so we // must ask the syncer to download them. Any newly supported datatypes will // not have been in that routing info list, so they will be among the types // downloaded if they are enabled. // // The SyncBackendRegistrar's state was initially derived from the types // detected to have been downloaded in the database. Afterwards it is // modified only by this function. We expect it to remain in sync with the // backend because configuration requests are never aborted; they are retried // until they succeed or the backend is shut down. syncer::ModelTypeSet types_to_download = registrar_->ConfigureDataTypes( types_to_add, types_to_remove); if (!types_to_download.Empty()) types_to_download.Put(syncer::NIGORI); // TODO(sync): crbug.com/137550. // It's dangerous to configure types that have progress markers. Types with // progress markers can trigger a MIGRATION_DONE response. We are not // prepared to handle a migration during a configure, so we must ensure that // all our types_to_download actually contain no data before we sync them. // // One common way to end up in this situation used to be types which // downloaded some or all of their data but have not applied it yet. We avoid // problems with those types by purging the data of any such partially synced // types soon after we load the directory. // // Another possible scenario is that we have newly supported or newly enabled // data types being downloaded here but the nigori type, which is always // included in any GetUpdates request, requires migration. The server has // code to detect this scenario based on the configure reason, the fact that // the nigori type is the only requested type which requires migration, and // that the requested types list includes at least one non-nigori type. It // will not send a MIGRATION_DONE response in that case. We still need to be // careful to not send progress markers for non-nigori types, though. If a // non-nigori type in the request requires migration, a MIGRATION_DONE // response will be sent. syncer::ModelSafeRoutingInfo routing_info; registrar_->GetModelSafeRoutingInfo(&routing_info); SDVLOG(1) << "Types " << syncer::ModelTypeSetToString(types_to_download) << " added; calling DoConfigureSyncer"; // TODO(zea): figure out how to bypass this call if no types are being // configured and GetKey is not needed. For now we rely on determining the // need for GetKey as part of the SyncManager::ConfigureSyncer logic. RequestConfigureSyncer(reason, types_to_download, routing_info, ready_task, retry_callback); } void SyncBackendHost::EnableEncryptEverything() { sync_thread_.message_loop()->PostTask(FROM_HERE, base::Bind(&SyncBackendHost::Core::DoEnableEncryptEverything, core_.get())); } void SyncBackendHost::ActivateDataType( syncer::ModelType type, syncer::ModelSafeGroup group, ChangeProcessor* change_processor) { registrar_->ActivateDataType(type, group, change_processor, GetUserShare()); } void SyncBackendHost::DeactivateDataType(syncer::ModelType type) { registrar_->DeactivateDataType(type); } syncer::UserShare* SyncBackendHost::GetUserShare() const { return core_->sync_manager()->GetUserShare(); } SyncBackendHost::Status SyncBackendHost::GetDetailedStatus() { DCHECK(initialized()); return core_->sync_manager()->GetDetailedStatus(); } SyncSessionSnapshot SyncBackendHost::GetLastSessionSnapshot() const { return last_snapshot_; } bool SyncBackendHost::HasUnsyncedItems() const { DCHECK(initialized()); return core_->sync_manager()->HasUnsyncedItems(); } bool SyncBackendHost::IsNigoriEnabled() const { return registrar_.get() && registrar_->IsNigoriEnabled(); } syncer::PassphraseType SyncBackendHost::GetPassphraseType() const { return cached_passphrase_type_; } base::Time SyncBackendHost::GetExplicitPassphraseTime() const { return cached_explicit_passphrase_time_; } bool SyncBackendHost::IsCryptographerReady( const syncer::BaseTransaction* trans) const { return initialized() && trans->GetCryptographer()->is_ready(); } void SyncBackendHost::GetModelSafeRoutingInfo( syncer::ModelSafeRoutingInfo* out) const { if (initialized()) { CHECK(registrar_.get()); registrar_->GetModelSafeRoutingInfo(out); } else { NOTREACHED(); } } SyncedDeviceTracker* SyncBackendHost::GetSyncedDeviceTracker() const { if (!initialized()) return NULL; return core_->synced_device_tracker(); } void SyncBackendHost::InitCore(const DoInitializeOptions& options) { sync_thread_.message_loop()->PostTask(FROM_HERE, base::Bind(&SyncBackendHost::Core::DoInitialize, core_.get(), options)); } void SyncBackendHost::RequestConfigureSyncer( syncer::ConfigureReason reason, syncer::ModelTypeSet types_to_config, const syncer::ModelSafeRoutingInfo& routing_info, const base::Callback& ready_task, const base::Closure& retry_callback) { sync_thread_.message_loop()->PostTask(FROM_HERE, base::Bind(&SyncBackendHost::Core::DoConfigureSyncer, core_.get(), reason, types_to_config, routing_info, ready_task, retry_callback)); } void SyncBackendHost::FinishConfigureDataTypesOnFrontendLoop( syncer::ModelTypeSet failed_configuration_types, const base::Callback& ready_task) { if (!frontend_) return; DCHECK_EQ(MessageLoop::current(), frontend_loop_); if (!ready_task.is_null()) ready_task.Run(failed_configuration_types); } void SyncBackendHost::HandleSyncManagerInitializationOnFrontendLoop( const syncer::WeakHandle& js_backend, const syncer::WeakHandle& debug_info_listener, syncer::ModelTypeSet restored_types) { DCHECK_EQ(initialization_state_, CREATING_SYNC_MANAGER); DCHECK(!js_backend_.IsInitialized()); initialization_state_ = INITIALIZATING_CONTROL_TYPES; js_backend_ = js_backend; debug_info_listener_ = debug_info_listener; // Inform the registrar of those types that have been fully downloaded and // applied. registrar_->SetInitialTypes(restored_types); // Kick off the next step in SyncBackendHost initialization by downloading // any necessary control types. sync_thread_.message_loop()->PostTask( FROM_HERE, base::Bind(&SyncBackendHost::Core::DoDownloadControlTypes, core_.get())); } SyncBackendHost::DoInitializeOptions::DoInitializeOptions( MessageLoop* sync_loop, SyncBackendRegistrar* registrar, const syncer::ModelSafeRoutingInfo& routing_info, const std::vector& workers, syncer::ExtensionsActivityMonitor* extensions_activity_monitor, const syncer::WeakHandle& event_handler, const GURL& service_url, MakeHttpBridgeFactoryFn make_http_bridge_factory_fn, const syncer::SyncCredentials& credentials, ChromeSyncNotificationBridge* chrome_sync_notification_bridge, syncer::InvalidatorFactory* invalidator_factory, syncer::SyncManagerFactory* sync_manager_factory, bool delete_sync_data_folder, const std::string& restored_key_for_bootstrapping, const std::string& restored_keystore_key_for_bootstrapping, InternalComponentsFactory* internal_components_factory, syncer::UnrecoverableErrorHandler* unrecoverable_error_handler, syncer::ReportUnrecoverableErrorFunction report_unrecoverable_error_function) : sync_loop(sync_loop), registrar(registrar), routing_info(routing_info), workers(workers), extensions_activity_monitor(extensions_activity_monitor), event_handler(event_handler), service_url(service_url), make_http_bridge_factory_fn(make_http_bridge_factory_fn), credentials(credentials), chrome_sync_notification_bridge(chrome_sync_notification_bridge), invalidator_factory(invalidator_factory), sync_manager_factory(sync_manager_factory), delete_sync_data_folder(delete_sync_data_folder), restored_key_for_bootstrapping(restored_key_for_bootstrapping), restored_keystore_key_for_bootstrapping( restored_keystore_key_for_bootstrapping), internal_components_factory(internal_components_factory), unrecoverable_error_handler(unrecoverable_error_handler), report_unrecoverable_error_function( report_unrecoverable_error_function) { } SyncBackendHost::DoInitializeOptions::~DoInitializeOptions() {} SyncBackendHost::Core::Core(const std::string& name, const FilePath& sync_data_folder_path, const base::WeakPtr& backend) : name_(name), sync_data_folder_path_(sync_data_folder_path), host_(backend), sync_loop_(NULL), registrar_(NULL), chrome_sync_notification_bridge_(NULL), registered_as_invalidation_handler_(false) { DCHECK(backend.get()); } SyncBackendHost::Core::~Core() { DCHECK(!sync_manager_.get()); DCHECK(!sync_loop_); } void SyncBackendHost::Core::OnSyncCycleCompleted( const SyncSessionSnapshot& snapshot) { if (!sync_loop_) return; DCHECK_EQ(MessageLoop::current(), sync_loop_); host_.Call( FROM_HERE, &SyncBackendHost::HandleSyncCycleCompletedOnFrontendLoop, snapshot); } void SyncBackendHost::Core::DoDownloadControlTypes() { syncer::ModelTypeSet new_control_types = registrar_->ConfigureDataTypes( syncer::ControlTypes(), syncer::ModelTypeSet()); syncer::ModelSafeRoutingInfo routing_info; registrar_->GetModelSafeRoutingInfo(&routing_info); SDVLOG(1) << "Control Types " << syncer::ModelTypeSetToString(new_control_types) << " added; calling DoConfigureSyncer"; sync_manager_->ConfigureSyncer( syncer::CONFIGURE_REASON_NEW_CLIENT, new_control_types, routing_info, base::Bind(&SyncBackendHost::Core::DoInitialProcessControlTypes, this), base::Bind(&SyncBackendHost::Core::OnControlTypesDownloadRetry, this)); } void SyncBackendHost::Core::OnControlTypesDownloadRetry() { host_.Call(FROM_HERE, &SyncBackendHost::HandleControlTypesDownloadRetry); } void SyncBackendHost::Core::OnInitializationComplete( const syncer::WeakHandle& js_backend, const syncer::WeakHandle& debug_info_listener, bool success, const syncer::ModelTypeSet restored_types) { DCHECK_EQ(MessageLoop::current(), sync_loop_); if (!success) { DoDestroySyncManager(); host_.Call(FROM_HERE, &SyncBackendHost::HandleInitializationCompletedOnFrontendLoop, false); return; } // Register for encryption related changes now. We have to do this before // the initializing downloading control types or initializing the encryption // handler in order to receive notifications triggered during encryption // startup. sync_manager_->GetEncryptionHandler()->AddObserver(this); // Sync manager initialization is complete, so we can schedule recurring // SaveChanges. sync_loop_->PostTask(FROM_HERE, base::Bind(&Core::StartSavingChanges, this)); host_.Call(FROM_HERE, &SyncBackendHost::HandleSyncManagerInitializationOnFrontendLoop, js_backend, debug_info_listener, restored_types); } void SyncBackendHost::Core::OnConnectionStatusChange( syncer::ConnectionStatus status) { if (!sync_loop_) return; DCHECK_EQ(MessageLoop::current(), sync_loop_); host_.Call( FROM_HERE, &SyncBackendHost::HandleConnectionStatusChangeOnFrontendLoop, status); } void SyncBackendHost::Core::OnPassphraseRequired( syncer::PassphraseRequiredReason reason, const sync_pb::EncryptedData& pending_keys) { if (!sync_loop_) return; DCHECK_EQ(MessageLoop::current(), sync_loop_); host_.Call( FROM_HERE, &SyncBackendHost::NotifyPassphraseRequired, reason, pending_keys); } void SyncBackendHost::Core::OnPassphraseAccepted() { if (!sync_loop_) return; DCHECK_EQ(MessageLoop::current(), sync_loop_); host_.Call( FROM_HERE, &SyncBackendHost::NotifyPassphraseAccepted); } void SyncBackendHost::Core::OnBootstrapTokenUpdated( const std::string& bootstrap_token, syncer::BootstrapTokenType type) { if (!sync_loop_) return; DCHECK_EQ(MessageLoop::current(), sync_loop_); host_.Call(FROM_HERE, &SyncBackendHost::PersistEncryptionBootstrapToken, bootstrap_token, type); } void SyncBackendHost::Core::OnStopSyncingPermanently() { if (!sync_loop_) return; DCHECK_EQ(MessageLoop::current(), sync_loop_); host_.Call( FROM_HERE, &SyncBackendHost::HandleStopSyncingPermanentlyOnFrontendLoop); } void SyncBackendHost::Core::OnUpdatedToken(const std::string& token) { if (!sync_loop_) return; DCHECK_EQ(MessageLoop::current(), sync_loop_); host_.Call( FROM_HERE, &SyncBackendHost::NotifyUpdatedToken, token); } void SyncBackendHost::Core::OnEncryptedTypesChanged( syncer::ModelTypeSet encrypted_types, bool encrypt_everything) { if (!sync_loop_) return; DCHECK_EQ(MessageLoop::current(), sync_loop_); // NOTE: We're in a transaction. host_.Call( FROM_HERE, &SyncBackendHost::NotifyEncryptedTypesChanged, encrypted_types, encrypt_everything); } void SyncBackendHost::Core::OnEncryptionComplete() { if (!sync_loop_) return; DCHECK_EQ(MessageLoop::current(), sync_loop_); // NOTE: We're in a transaction. host_.Call( FROM_HERE, &SyncBackendHost::NotifyEncryptionComplete); } void SyncBackendHost::Core::OnCryptographerStateChanged( syncer::Cryptographer* cryptographer) { // Do nothing. } void SyncBackendHost::Core::OnPassphraseTypeChanged( syncer::PassphraseType type, base::Time passphrase_time) { host_.Call( FROM_HERE, &SyncBackendHost::HandlePassphraseTypeChangedOnFrontendLoop, type, passphrase_time); } void SyncBackendHost::Core::OnActionableError( const syncer::SyncProtocolError& sync_error) { if (!sync_loop_) return; DCHECK_EQ(MessageLoop::current(), sync_loop_); host_.Call( FROM_HERE, &SyncBackendHost::HandleActionableErrorEventOnFrontendLoop, sync_error); } void SyncBackendHost::Core::OnInvalidatorStateChange( syncer::InvalidatorState state) { if (!sync_loop_) return; DCHECK_EQ(MessageLoop::current(), sync_loop_); host_.Call(FROM_HERE, &SyncBackendHost::HandleInvalidatorStateChangeOnFrontendLoop, state); } void SyncBackendHost::Core::OnIncomingInvalidation( const syncer::ObjectIdInvalidationMap& invalidation_map, syncer::IncomingInvalidationSource source) { if (!sync_loop_) return; DCHECK_EQ(MessageLoop::current(), sync_loop_); host_.Call(FROM_HERE, &SyncBackendHost::HandleIncomingInvalidationOnFrontendLoop, invalidation_map, source); } void SyncBackendHost::Core::DoInitialize(const DoInitializeOptions& options) { DCHECK(!sync_loop_); sync_loop_ = options.sync_loop; DCHECK(sync_loop_); // Blow away the partial or corrupt sync data folder before doing any more // initialization, if necessary. if (options.delete_sync_data_folder) { DeleteSyncDataFolder(); } // Make sure that the directory exists before initializing the backend. // If it already exists, this will do no harm. if (!file_util::CreateDirectory(sync_data_folder_path_)) { DLOG(FATAL) << "Sync Data directory creation failed."; } DCHECK(!registrar_); registrar_ = options.registrar; DCHECK(registrar_); DCHECK(!chrome_sync_notification_bridge_); chrome_sync_notification_bridge_ = options.chrome_sync_notification_bridge; DCHECK(chrome_sync_notification_bridge_); #if defined(OS_ANDROID) // Android uses ChromeSyncNotificationBridge exclusively. const syncer::InvalidatorState kDefaultInvalidatorState = syncer::INVALIDATIONS_ENABLED; #else const syncer::InvalidatorState kDefaultInvalidatorState = syncer::DEFAULT_INVALIDATION_ERROR; #endif sync_manager_ = options.sync_manager_factory->CreateSyncManager(name_); sync_manager_->AddObserver(this); sync_manager_->Init( sync_data_folder_path_, options.event_handler, options.service_url.host() + options.service_url.path(), options.service_url.EffectiveIntPort(), options.service_url.SchemeIsSecure(), options.make_http_bridge_factory_fn.Run().Pass(), options.workers, options.extensions_activity_monitor, options.registrar /* as SyncManager::ChangeDelegate */, options.credentials, scoped_ptr(new BridgedInvalidator( options.chrome_sync_notification_bridge, options.invalidator_factory->CreateInvalidator(), kDefaultInvalidatorState)), options.restored_key_for_bootstrapping, options.restored_keystore_key_for_bootstrapping, scoped_ptr( options.internal_components_factory), &encryptor_, options.unrecoverable_error_handler, options.report_unrecoverable_error_function); // |sync_manager_| may end up being NULL here in tests (in // synchronous initialization mode). // // TODO(akalin): Fix this behavior (see http://crbug.com/140354). if (sync_manager_.get()) { sync_manager_->RegisterInvalidationHandler(this); registered_as_invalidation_handler_ = true; // Now check the command line to see if we need to simulate an // unrecoverable error for testing purpose. Note the error is thrown // only if the initialization succeeded. Also it makes sense to use this // flag only when restarting the browser with an account already setup. If // you use this before setting up the setup would not succeed as an error // would be encountered. if (CommandLine::ForCurrentProcess()->HasSwitch( switches::kSyncThrowUnrecoverableError)) { sync_manager_->ThrowUnrecoverableError(); } } } void SyncBackendHost::Core::DoUpdateCredentials( const SyncCredentials& credentials) { DCHECK_EQ(MessageLoop::current(), sync_loop_); // UpdateCredentials can be called during backend initialization, possibly // when backend initialization has failed but hasn't notified the UI thread // yet. In that case, the sync manager may have been destroyed on the sync // thread before this task was executed, so we do nothing. if (!sync_manager_.get()) { sync_manager_->UpdateCredentials(credentials); } } void SyncBackendHost::Core::DoUpdateRegisteredInvalidationIds( const syncer::ObjectIdSet& ids) { DCHECK_EQ(MessageLoop::current(), sync_loop_); // |sync_manager_| may end up being NULL here in tests (in // synchronous initialization mode) since this is called during // shutdown. // // TODO(akalin): Fix this behavior (see http://crbug.com/140354). if (sync_manager_.get()) { sync_manager_->UpdateRegisteredInvalidationIds(this, ids); } } void SyncBackendHost::Core::DoStartSyncing( const syncer::ModelSafeRoutingInfo& routing_info) { DCHECK_EQ(MessageLoop::current(), sync_loop_); sync_manager_->StartSyncingNormally(routing_info); } void SyncBackendHost::Core::DoSetEncryptionPassphrase( const std::string& passphrase, bool is_explicit) { DCHECK_EQ(MessageLoop::current(), sync_loop_); sync_manager_->GetEncryptionHandler()->SetEncryptionPassphrase( passphrase, is_explicit); } void SyncBackendHost::Core::DoInitialProcessControlTypes() { DCHECK_EQ(MessageLoop::current(), sync_loop_); DVLOG(1) << "Initilalizing Control Types"; // Initialize encryption. sync_manager_->GetEncryptionHandler()->Init(); // Note: experiments are currently handled via SBH::AddExperimentalTypes, // which is called at the end of every sync cycle. // TODO(zea): eventually add an experiment handler and initialize it here. if (!sync_manager_->GetUserShare()) { // NULL in some tests. DVLOG(1) << "Skipping initialization of DeviceInfo"; host_.Call( FROM_HERE, &SyncBackendHost::HandleInitializationCompletedOnFrontendLoop, true); return; } if (!sync_manager_->InitialSyncEndedTypes().HasAll(syncer::ControlTypes())) { LOG(ERROR) << "Failed to download control types"; host_.Call( FROM_HERE, &SyncBackendHost::HandleInitializationCompletedOnFrontendLoop, false); return; } // Initialize device info. This is asynchronous on some platforms, so we // provide a callback for when it finishes. synced_device_tracker_.reset( new SyncedDeviceTracker(sync_manager_->GetUserShare(), sync_manager_->cache_guid())); synced_device_tracker_->InitLocalDeviceInfo( base::Bind(&SyncBackendHost::Core::DoFinishInitialProcessControlTypes, this)); } void SyncBackendHost::Core::DoFinishInitialProcessControlTypes() { registrar_->ActivateDataType(syncer::DEVICE_INFO, syncer::GROUP_PASSIVE, synced_device_tracker_.get(), sync_manager_->GetUserShare()); host_.Call( FROM_HERE, &SyncBackendHost::HandleInitializationCompletedOnFrontendLoop, true); } void SyncBackendHost::Core::DoSetDecryptionPassphrase( const std::string& passphrase) { DCHECK_EQ(MessageLoop::current(), sync_loop_); sync_manager_->GetEncryptionHandler()->SetDecryptionPassphrase( passphrase); } void SyncBackendHost::Core::DoEnableEncryptEverything() { DCHECK_EQ(MessageLoop::current(), sync_loop_); sync_manager_->GetEncryptionHandler()->EnableEncryptEverything(); } void SyncBackendHost::Core::DoStopSyncManagerForShutdown( const base::Closure& closure) { if (sync_manager_.get()) { sync_manager_->StopSyncingForShutdown(closure); } else { sync_loop_->PostTask(FROM_HERE, closure); } } void SyncBackendHost::Core::DoShutdown(bool sync_disabled) { DCHECK_EQ(MessageLoop::current(), sync_loop_); // It's safe to do this even if the type was never activated. registrar_->DeactivateDataType(syncer::DEVICE_INFO); synced_device_tracker_.reset(); DoDestroySyncManager(); chrome_sync_notification_bridge_ = NULL; registrar_ = NULL; if (sync_disabled) DeleteSyncDataFolder(); sync_loop_ = NULL; host_.Reset(); } void SyncBackendHost::Core::DoDestroySyncManager() { DCHECK_EQ(MessageLoop::current(), sync_loop_); if (sync_manager_.get()) { save_changes_timer_.reset(); if (registered_as_invalidation_handler_) { sync_manager_->UnregisterInvalidationHandler(this); registered_as_invalidation_handler_ = false; } sync_manager_->RemoveObserver(this); sync_manager_->ShutdownOnSyncThread(); sync_manager_.reset(); } } void SyncBackendHost::Core::DoConfigureSyncer( syncer::ConfigureReason reason, syncer::ModelTypeSet types_to_config, const syncer::ModelSafeRoutingInfo routing_info, const base::Callback& ready_task, const base::Closure& retry_callback) { DCHECK_EQ(MessageLoop::current(), sync_loop_); sync_manager_->ConfigureSyncer( reason, types_to_config, routing_info, base::Bind(&SyncBackendHost::Core::DoFinishConfigureDataTypes, this, types_to_config, ready_task), base::Bind(&SyncBackendHost::Core::DoRetryConfiguration, this, retry_callback)); } void SyncBackendHost::Core::DoFinishConfigureDataTypes( syncer::ModelTypeSet types_to_config, const base::Callback& ready_task) { DCHECK_EQ(MessageLoop::current(), sync_loop_); // Update the enabled types for the bridge and sync manager. syncer::ModelSafeRoutingInfo routing_info; registrar_->GetModelSafeRoutingInfo(&routing_info); const syncer::ModelTypeSet enabled_types = GetRoutingInfoTypes(routing_info); sync_manager_->UpdateEnabledTypes(enabled_types); const syncer::ModelTypeSet failed_configuration_types = Difference(types_to_config, sync_manager_->InitialSyncEndedTypes()); host_.Call(FROM_HERE, &SyncBackendHost::FinishConfigureDataTypesOnFrontendLoop, failed_configuration_types, ready_task); } void SyncBackendHost::Core::DoRetryConfiguration( const base::Closure& retry_callback) { DCHECK_EQ(MessageLoop::current(), sync_loop_); host_.Call(FROM_HERE, &SyncBackendHost::RetryConfigurationOnFrontendLoop, retry_callback); } void SyncBackendHost::Core::DeleteSyncDataFolder() { DCHECK_EQ(MessageLoop::current(), sync_loop_); if (file_util::DirectoryExists(sync_data_folder_path_)) { if (!file_util::Delete(sync_data_folder_path_, true)) SLOG(DFATAL) << "Could not delete the Sync Data folder."; } } void SyncBackendHost::Core::StartSavingChanges() { // We may already be shut down. if (!sync_loop_) return; DCHECK_EQ(MessageLoop::current(), sync_loop_); DCHECK(!save_changes_timer_.get()); save_changes_timer_.reset(new base::RepeatingTimer()); save_changes_timer_->Start(FROM_HERE, base::TimeDelta::FromSeconds(kSaveChangesIntervalSeconds), this, &Core::SaveChanges); } void SyncBackendHost::Core::SaveChanges() { DCHECK_EQ(MessageLoop::current(), sync_loop_); sync_manager_->SaveChanges(); } void SyncBackendHost::AddExperimentalTypes() { CHECK(initialized()); syncer::Experiments experiments; if (core_->sync_manager()->ReceivedExperiment(&experiments)) frontend_->OnExperimentsChanged(experiments); } void SyncBackendHost::HandleControlTypesDownloadRetry() { DCHECK_EQ(MessageLoop::current(), frontend_loop_); if (!frontend_) return; frontend_->OnSyncConfigureRetry(); } void SyncBackendHost::HandleInitializationCompletedOnFrontendLoop( bool success) { DCHECK_NE(initialization_state_, NOT_ATTEMPTED); if (!frontend_) return; DCHECK_EQ(MessageLoop::current(), frontend_loop_); if (!success) { js_backend_.Reset(); initialization_state_ = NOT_INITIALIZED; frontend_->OnBackendInitialized( syncer::WeakHandle(), syncer::WeakHandle(), false); return; } initialization_state_ = INITIALIZED; // Now that we've downloaded the control types, we can see if there are any // experimental types to enable. This should be done before we inform // the frontend to ensure they're visible in the customize screen. AddExperimentalTypes(); frontend_->OnBackendInitialized(js_backend_, debug_info_listener_, true); js_backend_.Reset(); } void SyncBackendHost::HandleSyncCycleCompletedOnFrontendLoop( const SyncSessionSnapshot& snapshot) { if (!frontend_) return; DCHECK_EQ(MessageLoop::current(), frontend_loop_); last_snapshot_ = snapshot; SDVLOG(1) << "Got snapshot " << snapshot.ToString(); const syncer::ModelTypeSet to_migrate = snapshot.model_neutral_state().types_needing_local_migration; if (!to_migrate.Empty()) frontend_->OnMigrationNeededForTypes(to_migrate); // Process any changes to the datatypes we're syncing. // TODO(sync): add support for removing types. if (initialized()) AddExperimentalTypes(); if (initialized()) frontend_->OnSyncCycleCompleted(); } void SyncBackendHost::RetryConfigurationOnFrontendLoop( const base::Closure& retry_callback) { SDVLOG(1) << "Failed to complete configuration, informing of retry."; retry_callback.Run(); } void SyncBackendHost::PersistEncryptionBootstrapToken( const std::string& token, syncer::BootstrapTokenType token_type) { CHECK(sync_prefs_.get()); DCHECK(!token.empty()); if (token_type == syncer::PASSPHRASE_BOOTSTRAP_TOKEN) sync_prefs_->SetEncryptionBootstrapToken(token); else sync_prefs_->SetKeystoreEncryptionBootstrapToken(token); } void SyncBackendHost::HandleActionableErrorEventOnFrontendLoop( const syncer::SyncProtocolError& sync_error) { if (!frontend_) return; DCHECK_EQ(MessageLoop::current(), frontend_loop_); frontend_->OnActionableError(sync_error); } void SyncBackendHost::HandleInvalidatorStateChangeOnFrontendLoop( syncer::InvalidatorState state) { if (!frontend_) return; DCHECK_EQ(MessageLoop::current(), frontend_loop_); frontend_->OnInvalidatorStateChange(state); } void SyncBackendHost::HandleIncomingInvalidationOnFrontendLoop( const syncer::ObjectIdInvalidationMap& invalidation_map, syncer::IncomingInvalidationSource source) { if (!frontend_) return; DCHECK_EQ(MessageLoop::current(), frontend_loop_); frontend_->OnIncomingInvalidation(invalidation_map, source); } bool SyncBackendHost::CheckPassphraseAgainstCachedPendingKeys( const std::string& passphrase) const { DCHECK(cached_pending_keys_.has_blob()); DCHECK(!passphrase.empty()); syncer::Nigori nigori; nigori.InitByDerivation("localhost", "dummy", passphrase); std::string plaintext; bool result = nigori.Decrypt(cached_pending_keys_.blob(), &plaintext); DVLOG_IF(1, result) << "Passphrase failed to decrypt pending keys."; return result; } void SyncBackendHost::NotifyPassphraseRequired( syncer::PassphraseRequiredReason reason, sync_pb::EncryptedData pending_keys) { if (!frontend_) return; DCHECK_EQ(MessageLoop::current(), frontend_loop_); // Update our cache of the cryptographer's pending keys. cached_pending_keys_ = pending_keys; frontend_->OnPassphraseRequired(reason, pending_keys); } void SyncBackendHost::NotifyPassphraseAccepted() { if (!frontend_) return; DCHECK_EQ(MessageLoop::current(), frontend_loop_); // Clear our cache of the cryptographer's pending keys. cached_pending_keys_.clear_blob(); frontend_->OnPassphraseAccepted(); } void SyncBackendHost::NotifyUpdatedToken(const std::string& token) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); TokenAvailableDetails details(GaiaConstants::kSyncService, token); TokenService* token_service = TokenServiceFactory::GetForProfile(profile_); CHECK(token_service); token_service->AddAuthTokenManually(details.service(), details.token()); } void SyncBackendHost::NotifyEncryptedTypesChanged( syncer::ModelTypeSet encrypted_types, bool encrypt_everything) { if (!frontend_) return; DCHECK_EQ(MessageLoop::current(), frontend_loop_); frontend_->OnEncryptedTypesChanged( encrypted_types, encrypt_everything); } void SyncBackendHost::NotifyEncryptionComplete() { if (!frontend_) return; DCHECK_EQ(MessageLoop::current(), frontend_loop_); frontend_->OnEncryptionComplete(); } void SyncBackendHost::HandlePassphraseTypeChangedOnFrontendLoop( syncer::PassphraseType type, base::Time explicit_passphrase_time) { DCHECK_EQ(MessageLoop::current(), frontend_loop_); DVLOG(1) << "Passphrase type changed to " << syncer::PassphraseTypeToString(type); cached_passphrase_type_ = type; cached_explicit_passphrase_time_ = explicit_passphrase_time; } void SyncBackendHost::HandleStopSyncingPermanentlyOnFrontendLoop() { if (!frontend_) return; frontend_->OnStopSyncingPermanently(); } void SyncBackendHost::HandleConnectionStatusChangeOnFrontendLoop( syncer::ConnectionStatus status) { if (!frontend_) return; DCHECK_EQ(MessageLoop::current(), frontend_loop_); DVLOG(1) << "Connection status changed: " << syncer::ConnectionStatusToString(status); frontend_->OnConnectionStatusChange(status); } #undef SDVLOG #undef SLOG } // namespace browser_sync