// 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 "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_util.h" #include "base/files/file_path.h" #include "base/location.h" #include "base/metrics/histogram.h" #include "base/strings/utf_string_conversions.h" #include "base/threading/thread_restrictions.h" #include "base/timer/timer.h" #include "base/tracked_objects.h" #include "build/build_config.h" #include "chrome/browser/invalidation/invalidation_service.h" #include "chrome/browser/invalidation/invalidation_service_factory.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/net/network_time_tracker.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/change_processor.h" #include "chrome/browser/sync/glue/chrome_encryptor.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/sync_prefs.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_details.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/notification_source.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/cancelation_signal.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/protocol/encryption.pb.h" #include "sync/protocol/sync.pb.h" #include "sync/util/nigori.h" static const int kSaveChangesIntervalSeconds = 10; static const base::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; namespace { // Helper struct to handle currying params to // SyncBackendHost::Core::DoConfigureSyncer. struct DoConfigureSyncerTypes { DoConfigureSyncerTypes() {} ~DoConfigureSyncerTypes() {} syncer::ModelTypeSet to_download; syncer::ModelTypeSet to_purge; syncer::ModelTypeSet to_journal; syncer::ModelTypeSet to_unapply; }; } // namespace // 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: Core(const std::string& name, const base::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; // Forwards an invalidation state change to the sync manager. void DoOnInvalidatorStateChange(syncer::InvalidatorState state); // Forwards an invalidation to the sync manager. void DoOnIncomingInvalidation( syncer::ObjectIdInvalidationMap invalidation_map); // 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(scoped_ptr options); // Called to perform credential update on behalf of // SyncBackendHost::UpdateCredentials. void DoUpdateCredentials(const syncer::SyncCredentials& credentials); // 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(syncer::ConfigureReason reason); // Ask the syncer to check for updates for the specified types. void DoRefreshTypes(syncer::ModelTypeSet types); // 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) Call ShutdownOnUIThread() from |frontend_loop_| to request sync manager // to stop as soon as possible. // 2) Post DoShutdown() to sync loop to clean up backend state, save // directory and destroy sync manager. void ShutdownOnUIThread(); void DoShutdown(bool sync_disabled); void DoDestroySyncManager(); // Configuration methods that must execute on sync loop. void DoConfigureSyncer( syncer::ConfigureReason reason, const DoConfigureSyncerTypes& config_types, 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(); // We expose this member because it's required in the construction of the // HttpBridgeFactory. syncer::CancelationSignal* GetRequestContextCancelationSignal() { return &release_request_context_signal_; } 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 |registrar_->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 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 base::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 ~Core(). base::MessageLoop* sync_loop_; // Our parent's registrar (not owned). Non-NULL only between // calls to DoInitialize() and DoShutdown(). SyncBackendRegistrar* registrar_; // 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_; base::WeakPtrFactory weak_ptr_factory_; // These signals allow us to send requests to shut down the HttpBridgeFactory // and ServerConnectionManager without having to wait for those classes to // finish initializing first. // // See comments in Core::ShutdownOnUIThread() for more details. syncer::CancelationSignal release_request_context_signal_; syncer::CancelationSignal stop_syncing_signal_; DISALLOW_COPY_AND_ASSIGN(Core); }; SyncBackendHost::SyncBackendHost( const std::string& name, Profile* profile, const base::WeakPtr& sync_prefs) : weak_ptr_factory_(this), frontend_loop_(base::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), frontend_(NULL), cached_passphrase_type_(syncer::IMPLICIT_PASSPHRASE), invalidator_( invalidation::InvalidationServiceFactory::GetForProfile(profile)), invalidation_handler_registered_(false) { } SyncBackendHost::SyncBackendHost(Profile* profile) : weak_ptr_factory_(this), frontend_loop_(base::MessageLoop::current()), profile_(profile), name_("Unknown"), initialization_state_(NOT_ATTEMPTED), frontend_(NULL), cached_passphrase_type_(syncer::IMPLICIT_PASSPHRASE), invalidation_handler_registered_(false) { } SyncBackendHost::~SyncBackendHost() { DCHECK(!core_.get() && !frontend_) << "Must call Shutdown before destructor."; DCHECK(!registrar_.get()); } void SyncBackendHost::Initialize( SyncFrontend* frontend, scoped_ptr sync_thread, const syncer::WeakHandle& event_handler, const GURL& sync_service_url, const SyncCredentials& credentials, bool delete_sync_data_folder, scoped_ptr sync_manager_factory, scoped_ptr unrecoverable_error_handler, syncer::ReportUnrecoverableErrorFunction report_unrecoverable_error_function) { registrar_.reset(new SyncBackendRegistrar(name_, profile_, sync_thread.Pass())); CHECK(registrar_->sync_thread()); frontend_ = frontend; DCHECK(frontend); syncer::ModelSafeRoutingInfo routing_info; std::vector workers; registrar_->GetModelSafeRoutingInfo(&routing_info); registrar_->GetWorkers(&workers); InternalComponentsFactory::Switches factory_switches = { InternalComponentsFactory::ENCRYPTION_KEYSTORE, InternalComponentsFactory::BACKOFF_NORMAL }; CommandLine* cl = CommandLine::ForCurrentProcess(); if (cl->HasSwitch(switches::kSyncShortInitialRetryOverride)) { factory_switches.backoff_override = InternalComponentsFactoryImpl::BACKOFF_SHORT_INITIAL_RETRY_OVERRIDE; } if (cl->HasSwitch(switches::kSyncEnableGetUpdateAvoidance)) { factory_switches.pre_commit_updates_policy = InternalComponentsFactoryImpl::FORCE_ENABLE_PRE_COMMIT_UPDATE_AVOIDANCE; } initialization_state_ = CREATING_SYNC_MANAGER; scoped_ptr init_opts(new DoInitializeOptions( registrar_->sync_thread()->message_loop(), registrar_.get(), routing_info, workers, extensions_activity_monitor_.GetExtensionsActivity(), event_handler, sync_service_url, scoped_ptr( new syncer::HttpBridgeFactory( make_scoped_refptr(profile_->GetRequestContext()), NetworkTimeTracker::BuildNotifierUpdateCallback(), core_->GetRequestContextCancelationSignal())), credentials, invalidator_->GetInvalidatorClientId(), sync_manager_factory.Pass(), delete_sync_data_folder, sync_prefs_->GetEncryptionBootstrapToken(), sync_prefs_->GetKeystoreEncryptionBootstrapToken(), scoped_ptr( new InternalComponentsFactoryImpl(factory_switches)).Pass(), unrecoverable_error_handler.Pass(), report_unrecoverable_error_function, !cl->HasSwitch(switches::kSyncDisableOAuth2Token))); InitCore(init_opts.Pass()); } void SyncBackendHost::UpdateCredentials(const SyncCredentials& credentials) { DCHECK(registrar_->sync_thread()->IsRunning()); registrar_->sync_thread()->message_loop()->PostTask(FROM_HERE, base::Bind(&SyncBackendHost::Core::DoUpdateCredentials, core_.get(), credentials)); } void SyncBackendHost::StartSyncingWithServer() { SDVLOG(1) << "SyncBackendHost::StartSyncingWithServer called."; syncer::ModelSafeRoutingInfo routing_info; registrar_->GetModelSafeRoutingInfo(&routing_info); registrar_->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(registrar_->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(base::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. registrar_->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(base::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. registrar_->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::StopSyncingForShutdown() { DCHECK_EQ(base::MessageLoop::current(), frontend_loop_); DCHECK_GT(initialization_state_, NOT_ATTEMPTED); // Immediately stop sending messages to the frontend. frontend_ = NULL; // Stop listening for and forwarding locally-triggered sync refresh requests. notification_registrar_.RemoveAll(); DCHECK(registrar_->sync_thread()->IsRunning()); registrar_->RequestWorkerStopOnUIThread(); core_->ShutdownOnUIThread(); } scoped_ptr SyncBackendHost::Shutdown(ShutdownOption option) { // StopSyncingForShutdown() (which nulls out |frontend_|) should be // called first. DCHECK(!frontend_); DCHECK(registrar_->sync_thread()->IsRunning()); bool sync_disabled = (option == DISABLE_AND_CLAIM_THREAD); bool sync_thread_claimed = (option == DISABLE_AND_CLAIM_THREAD || option == STOP_AND_CLAIM_THREAD); if (invalidation_handler_registered_) { if (sync_disabled) { UnregisterInvalidationIds(); } invalidator_->UnregisterInvalidationHandler(this); invalidator_ = NULL; } invalidation_handler_registered_ = false; // Shut down and destroy sync manager. registrar_->sync_thread()->message_loop()->PostTask( FROM_HERE, base::Bind(&SyncBackendHost::Core::DoShutdown, core_.get(), sync_disabled)); core_ = NULL; // Worker cleanup. SyncBackendRegistrar* detached_registrar = registrar_.release(); detached_registrar->sync_thread()->message_loop()->PostTask( FROM_HERE, base::Bind(&SyncBackendRegistrar::Shutdown, base::Unretained(detached_registrar))); js_backend_.Reset(); if (sync_thread_claimed) return detached_registrar->ReleaseSyncThread(); else return scoped_ptr(); } void SyncBackendHost::UnregisterInvalidationIds() { if (invalidation_handler_registered_) { invalidator_->UpdateRegisteredInvalidationIds( this, syncer::ObjectIdSet()); } } void SyncBackendHost::ConfigureDataTypes( syncer::ConfigureReason reason, const DataTypeConfigStateMap& config_state_map, 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 previous_types = registrar_->GetLastConfiguredTypes(); syncer::ModelTypeSet disabled_types = GetDataTypesInState(DISABLED, config_state_map); syncer::ModelTypeSet fatal_types = GetDataTypesInState(FATAL, config_state_map); syncer::ModelTypeSet crypto_types = GetDataTypesInState(CRYPTO, config_state_map); disabled_types.PutAll(fatal_types); disabled_types.PutAll(crypto_types); syncer::ModelTypeSet active_types = GetDataTypesInState(CONFIGURE_ACTIVE, config_state_map); syncer::ModelTypeSet clean_first_types = GetDataTypesInState(CONFIGURE_CLEAN, config_state_map); syncer::ModelTypeSet types_to_download = registrar_->ConfigureDataTypes( syncer::Union(active_types, clean_first_types), disabled_types); types_to_download.PutAll(clean_first_types); types_to_download.RemoveAll(syncer::ProxyTypes()); 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); syncer::ModelTypeSet current_types = registrar_->GetLastConfiguredTypes(); syncer::ModelTypeSet types_to_purge = syncer::Difference(previous_types, current_types); syncer::ModelTypeSet inactive_types = GetDataTypesInState(CONFIGURE_INACTIVE, config_state_map); types_to_purge.RemoveAll(inactive_types); // If a type has already been disabled and unapplied or journaled, it will // not be part of the |types_to_purge| set, and therefore does not need // to be acted on again. fatal_types.RetainAll(types_to_purge); syncer::ModelTypeSet unapply_types = syncer::Union(crypto_types, clean_first_types); unapply_types.RetainAll(types_to_purge); DCHECK(syncer::Intersection(current_types, fatal_types).Empty()); DCHECK(syncer::Intersection(current_types, crypto_types).Empty()); DCHECK(current_types.HasAll(types_to_download)); SDVLOG(1) << "Types " << syncer::ModelTypeSetToString(types_to_download) << " added; calling DoConfigureSyncer"; // Divide up the types into their corresponding actions (each is mutually // exclusive): // - Types which have just been added to the routing info (types_to_download): // are downloaded. // - Types which have encountered a fatal error (fatal_types) are deleted // from the directory and journaled in the delete journal. // - Types which have encountered a cryptographer error (crypto_types) are // unapplied (local state is purged but sync state is not). // - All other types not in the routing info (types just disabled) are deleted // from the directory. // - Everything else (enabled types and already disabled types) is not // touched. RequestConfigureSyncer(reason, types_to_download, types_to_purge, fatal_types, unapply_types, inactive_types, routing_info, ready_task, retry_callback); } void SyncBackendHost::EnableEncryptEverything() { registrar_->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(scoped_ptr options) { registrar_->sync_thread()->message_loop()->PostTask(FROM_HERE, base::Bind(&SyncBackendHost::Core::DoInitialize, core_.get(), base::Passed(&options))); } void SyncBackendHost::RequestConfigureSyncer( syncer::ConfigureReason reason, syncer::ModelTypeSet to_download, syncer::ModelTypeSet to_purge, syncer::ModelTypeSet to_journal, syncer::ModelTypeSet to_unapply, syncer::ModelTypeSet to_ignore, const syncer::ModelSafeRoutingInfo& routing_info, const base::Callback& ready_task, const base::Closure& retry_callback) { DoConfigureSyncerTypes config_types; config_types.to_download = to_download; config_types.to_purge = to_purge; config_types.to_journal = to_journal; config_types.to_unapply = to_unapply; registrar_->sync_thread()->message_loop()->PostTask(FROM_HERE, base::Bind(&SyncBackendHost::Core::DoConfigureSyncer, core_.get(), reason, config_types, routing_info, ready_task, retry_callback)); } void SyncBackendHost::FinishConfigureDataTypesOnFrontendLoop( const syncer::ModelTypeSet enabled_types, const syncer::ModelTypeSet succeeded_configuration_types, const syncer::ModelTypeSet failed_configuration_types, const base::Callback& ready_task) { if (!frontend_) return; invalidator_->UpdateRegisteredInvalidationIds( this, ModelTypeSetToObjectIdSet(enabled_types)); if (!ready_task.is_null()) ready_task.Run(succeeded_configuration_types, 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; invalidator_->RegisterInvalidationHandler(this); invalidation_handler_registered_ = true; // Inform the registrar of those types that have been fully downloaded and // applied. registrar_->SetInitialTypes(restored_types); // Start forwarding refresh requests to the SyncManager notification_registrar_.Add(this, chrome::NOTIFICATION_SYNC_REFRESH_LOCAL, content::Source(profile_)); syncer::ConfigureReason reason = (sync_prefs_->HasSyncSetupCompleted() ? syncer::CONFIGURE_REASON_NEWLY_ENABLED_DATA_TYPE : syncer::CONFIGURE_REASON_NEW_CLIENT); // Fake a state change to initialize the SyncManager's cached invalidator // state. OnInvalidatorStateChange(invalidator_->GetInvalidatorState()); // Kick off the next step in SyncBackendHost initialization by downloading // any necessary control types. registrar_->sync_thread()->message_loop()->PostTask( FROM_HERE, base::Bind(&SyncBackendHost::Core::DoDownloadControlTypes, core_.get(), reason)); } void SyncBackendHost::Observe( int type, const content::NotificationSource& source, const content::NotificationDetails& details) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); DCHECK_EQ(type, chrome::NOTIFICATION_SYNC_REFRESH_LOCAL); content::Details state_details(details); const syncer::ModelTypeSet& types = *(state_details.ptr()); registrar_->sync_thread()->message_loop()->PostTask(FROM_HERE, base::Bind(&SyncBackendHost::Core::DoRefreshTypes, core_.get(), types)); } SyncBackendHost::DoInitializeOptions::DoInitializeOptions( base::MessageLoop* sync_loop, SyncBackendRegistrar* registrar, const syncer::ModelSafeRoutingInfo& routing_info, const std::vector& workers, const scoped_refptr& extensions_activity, const syncer::WeakHandle& event_handler, const GURL& service_url, scoped_ptr http_bridge_factory, const syncer::SyncCredentials& credentials, const std::string& invalidator_client_id, scoped_ptr sync_manager_factory, bool delete_sync_data_folder, const std::string& restored_key_for_bootstrapping, const std::string& restored_keystore_key_for_bootstrapping, scoped_ptr internal_components_factory, scoped_ptr unrecoverable_error_handler, syncer::ReportUnrecoverableErrorFunction report_unrecoverable_error_function, bool use_oauth2_token) : sync_loop(sync_loop), registrar(registrar), routing_info(routing_info), workers(workers), extensions_activity(extensions_activity), event_handler(event_handler), service_url(service_url), http_bridge_factory(http_bridge_factory.Pass()), credentials(credentials), invalidator_client_id(invalidator_client_id), sync_manager_factory(sync_manager_factory.Pass()), 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.Pass()), unrecoverable_error_handler(unrecoverable_error_handler.Pass()), report_unrecoverable_error_function( report_unrecoverable_error_function), use_oauth2_token(use_oauth2_token) { } SyncBackendHost::DoInitializeOptions::~DoInitializeOptions() {} SyncBackendHost::Core::Core(const std::string& name, const base::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), weak_ptr_factory_(this) { DCHECK(backend.get()); } SyncBackendHost::Core::~Core() { DCHECK(!sync_manager_.get()); } void SyncBackendHost::Core::OnSyncCycleCompleted( const SyncSessionSnapshot& snapshot) { if (!sync_loop_) return; DCHECK_EQ(base::MessageLoop::current(), sync_loop_); host_.Call( FROM_HERE, &SyncBackendHost::HandleSyncCycleCompletedOnFrontendLoop, snapshot); } void SyncBackendHost::Core::DoDownloadControlTypes( syncer::ConfigureReason reason) { 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 ConfigureSyncer"; syncer::ModelTypeSet types_to_purge = syncer::Difference(syncer::ModelTypeSet::All(), GetRoutingInfoTypes(routing_info)); sync_manager_->ConfigureSyncer( reason, new_control_types, types_to_purge, syncer::ModelTypeSet(), syncer::ModelTypeSet(), routing_info, base::Bind(&SyncBackendHost::Core::DoInitialProcessControlTypes, weak_ptr_factory_.GetWeakPtr()), base::Bind(&SyncBackendHost::Core::OnControlTypesDownloadRetry, weak_ptr_factory_.GetWeakPtr())); } void SyncBackendHost::Core::DoRefreshTypes(syncer::ModelTypeSet types) { DCHECK_EQ(base::MessageLoop::current(), sync_loop_); sync_manager_->RefreshTypes(types); } 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(base::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, weak_ptr_factory_.GetWeakPtr())); 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(base::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(base::MessageLoop::current(), sync_loop_); host_.Call( FROM_HERE, &SyncBackendHost::NotifyPassphraseRequired, reason, pending_keys); } void SyncBackendHost::Core::OnPassphraseAccepted() { if (!sync_loop_) return; DCHECK_EQ(base::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(base::MessageLoop::current(), sync_loop_); host_.Call(FROM_HERE, &SyncBackendHost::PersistEncryptionBootstrapToken, bootstrap_token, type); } void SyncBackendHost::Core::OnStopSyncingPermanently() { if (!sync_loop_) return; DCHECK_EQ(base::MessageLoop::current(), sync_loop_); host_.Call( FROM_HERE, &SyncBackendHost::HandleStopSyncingPermanentlyOnFrontendLoop); } void SyncBackendHost::Core::OnUpdatedToken(const std::string& token) { if (!sync_loop_) return; DCHECK_EQ(base::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(base::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(base::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(base::MessageLoop::current(), sync_loop_); host_.Call( FROM_HERE, &SyncBackendHost::HandleActionableErrorEventOnFrontendLoop, sync_error); } void SyncBackendHost::Core::DoOnInvalidatorStateChange( syncer::InvalidatorState state) { DCHECK_EQ(base::MessageLoop::current(), sync_loop_); sync_manager_->OnInvalidatorStateChange(state); } void SyncBackendHost::Core::DoOnIncomingInvalidation( syncer::ObjectIdInvalidationMap invalidation_map) { DCHECK_EQ(base::MessageLoop::current(), sync_loop_); sync_manager_->OnIncomingInvalidation(invalidation_map); } void SyncBackendHost::Core::DoInitialize( scoped_ptr options) { DCHECK(!sync_loop_); sync_loop_ = options->sync_loop; DCHECK(sync_loop_); // Finish initializing the HttpBridgeFactory. We do this here because // building the user agent may block on some platforms. chrome::VersionInfo version_info; options->http_bridge_factory->Init( DeviceInfo::MakeUserAgentForSyncApi(version_info)); // 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_); 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->http_bridge_factory.Pass(), options->workers, options->extensions_activity, options->registrar /* as SyncManager::ChangeDelegate */, options->credentials, options->invalidator_client_id, options->restored_key_for_bootstrapping, options->restored_keystore_key_for_bootstrapping, options->internal_components_factory.get(), &encryptor_, options->unrecoverable_error_handler.Pass(), options->report_unrecoverable_error_function, options->use_oauth2_token, &stop_syncing_signal_); // |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_) { // 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(base::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_) { sync_manager_->UpdateCredentials(credentials); } } void SyncBackendHost::Core::DoStartSyncing( const syncer::ModelSafeRoutingInfo& routing_info) { DCHECK_EQ(base::MessageLoop::current(), sync_loop_); sync_manager_->StartSyncingNormally(routing_info); } void SyncBackendHost::Core::DoSetEncryptionPassphrase( const std::string& passphrase, bool is_explicit) { DCHECK_EQ(base::MessageLoop::current(), sync_loop_); sync_manager_->GetEncryptionHandler()->SetEncryptionPassphrase( passphrase, is_explicit); } void SyncBackendHost::Core::DoInitialProcessControlTypes() { DCHECK_EQ(base::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, weak_ptr_factory_.GetWeakPtr())); } 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(base::MessageLoop::current(), sync_loop_); sync_manager_->GetEncryptionHandler()->SetDecryptionPassphrase( passphrase); } void SyncBackendHost::Core::DoEnableEncryptEverything() { DCHECK_EQ(base::MessageLoop::current(), sync_loop_); sync_manager_->GetEncryptionHandler()->EnableEncryptEverything(); } void SyncBackendHost::Core::ShutdownOnUIThread() { // This will cut short any blocking network tasks, cut short any in-progress // sync cycles, and prevent the creation of new blocking network tasks and new // sync cycles. If there was an in-progress network request, it would have // had a reference to the RequestContextGetter. This reference will be // dropped by the time this function returns. // // It is safe to call this even if Sync's backend classes have not been // initialized yet. Those classes will receive the message when the sync // thread finally getes around to constructing them. stop_syncing_signal_.Signal(); // This will drop the HttpBridgeFactory's reference to the // RequestContextGetter. Once this has been called, the HttpBridgeFactory can // no longer be used to create new HttpBridge instances. We can get away with // this because the stop_syncing_signal_ has already been signalled, which // guarantees that the ServerConnectionManager will no longer attempt to // create new connections. release_request_context_signal_.Signal(); } void SyncBackendHost::Core::DoShutdown(bool sync_disabled) { DCHECK_EQ(base::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(); registrar_ = NULL; if (sync_disabled) DeleteSyncDataFolder(); host_.Reset(); weak_ptr_factory_.InvalidateWeakPtrs(); } void SyncBackendHost::Core::DoDestroySyncManager() { DCHECK_EQ(base::MessageLoop::current(), sync_loop_); if (sync_manager_) { save_changes_timer_.reset(); sync_manager_->RemoveObserver(this); sync_manager_->ShutdownOnSyncThread(); sync_manager_.reset(); } } void SyncBackendHost::Core::DoConfigureSyncer( syncer::ConfigureReason reason, const DoConfigureSyncerTypes& config_types, const syncer::ModelSafeRoutingInfo routing_info, const base::Callback& ready_task, const base::Closure& retry_callback) { DCHECK_EQ(base::MessageLoop::current(), sync_loop_); sync_manager_->ConfigureSyncer( reason, config_types.to_download, config_types.to_purge, config_types.to_journal, config_types.to_unapply, routing_info, base::Bind(&SyncBackendHost::Core::DoFinishConfigureDataTypes, weak_ptr_factory_.GetWeakPtr(), config_types.to_download, ready_task), base::Bind(&SyncBackendHost::Core::DoRetryConfiguration, weak_ptr_factory_.GetWeakPtr(), retry_callback)); } void SyncBackendHost::Core::DoFinishConfigureDataTypes( syncer::ModelTypeSet types_to_config, const base::Callback& ready_task) { DCHECK_EQ(base::MessageLoop::current(), sync_loop_); // Update the enabled types for the bridge and sync manager. syncer::ModelSafeRoutingInfo routing_info; registrar_->GetModelSafeRoutingInfo(&routing_info); syncer::ModelTypeSet enabled_types = GetRoutingInfoTypes(routing_info); enabled_types.RemoveAll(syncer::ProxyTypes()); const syncer::ModelTypeSet failed_configuration_types = Difference(types_to_config, sync_manager_->InitialSyncEndedTypes()); const syncer::ModelTypeSet succeeded_configuration_types = Difference(types_to_config, failed_configuration_types); host_.Call(FROM_HERE, &SyncBackendHost::FinishConfigureDataTypesOnFrontendLoop, enabled_types, succeeded_configuration_types, failed_configuration_types, ready_task); } void SyncBackendHost::Core::DoRetryConfiguration( const base::Closure& retry_callback) { DCHECK_EQ(base::MessageLoop::current(), sync_loop_); host_.Call(FROM_HERE, &SyncBackendHost::RetryConfigurationOnFrontendLoop, retry_callback); } void SyncBackendHost::Core::DeleteSyncDataFolder() { DCHECK_EQ(base::MessageLoop::current(), sync_loop_); if (base::DirectoryExists(sync_data_folder_path_)) { if (!base::DeleteFile(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(base::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(base::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(base::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(base::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(base::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(base::MessageLoop::current(), frontend_loop_); frontend_->OnActionableError(sync_error); } void SyncBackendHost::OnInvalidatorStateChange(syncer::InvalidatorState state) { registrar_->sync_thread()->message_loop()->PostTask( FROM_HERE, base::Bind(&SyncBackendHost::Core::DoOnInvalidatorStateChange, core_.get(), state)); } void SyncBackendHost::OnIncomingInvalidation( const syncer::ObjectIdInvalidationMap& invalidation_map) { // TODO(dcheng): Acknowledge immediately for now. Fix this once the // invalidator doesn't repeatedly ping for unacknowledged invaliations, since // it conflicts with the sync scheduler's internal backoff algorithm. // See http://crbug.com/124149 for more information. for (syncer::ObjectIdInvalidationMap::const_iterator it = invalidation_map.begin(); it != invalidation_map.end(); ++it) { invalidator_->AcknowledgeInvalidation(it->first, it->second.ack_handle); } registrar_->sync_thread()->message_loop()->PostTask( FROM_HERE, base::Bind(&SyncBackendHost::Core::DoOnIncomingInvalidation, core_.get(), invalidation_map)); } 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(base::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(base::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(base::MessageLoop::current(), frontend_loop_); frontend_->OnEncryptedTypesChanged( encrypted_types, encrypt_everything); } void SyncBackendHost::NotifyEncryptionComplete() { if (!frontend_) return; DCHECK_EQ(base::MessageLoop::current(), frontend_loop_); frontend_->OnEncryptionComplete(); } void SyncBackendHost::HandlePassphraseTypeChangedOnFrontendLoop( syncer::PassphraseType type, base::Time explicit_passphrase_time) { DCHECK_EQ(base::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(base::MessageLoop::current(), frontend_loop_); DVLOG(1) << "Connection status changed: " << syncer::ConnectionStatusToString(status); frontend_->OnConnectionStatusChange(status); } base::MessageLoop* SyncBackendHost::GetSyncLoopForTesting() { return registrar_->sync_thread()->message_loop(); } #undef SDVLOG #undef SLOG } // namespace browser_sync