1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "sync/internal_api/sync_manager_impl.h"
9 #include "base/base64.h"
10 #include "base/bind.h"
11 #include "base/callback.h"
12 #include "base/compiler_specific.h"
13 #include "base/json/json_writer.h"
14 #include "base/memory/ref_counted.h"
15 #include "base/metrics/histogram.h"
16 #include "base/observer_list.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/values.h"
19 #include "sync/engine/sync_scheduler.h"
20 #include "sync/engine/syncer_types.h"
21 #include "sync/internal_api/change_reorder_buffer.h"
22 #include "sync/internal_api/public/base/cancelation_signal.h"
23 #include "sync/internal_api/public/base/model_type.h"
24 #include "sync/internal_api/public/base_node.h"
25 #include "sync/internal_api/public/configure_reason.h"
26 #include "sync/internal_api/public/engine/polling_constants.h"
27 #include "sync/internal_api/public/http_post_provider_factory.h"
28 #include "sync/internal_api/public/internal_components_factory.h"
29 #include "sync/internal_api/public/read_node.h"
30 #include "sync/internal_api/public/read_transaction.h"
31 #include "sync/internal_api/public/user_share.h"
32 #include "sync/internal_api/public/util/experiments.h"
33 #include "sync/internal_api/public/write_node.h"
34 #include "sync/internal_api/public/write_transaction.h"
35 #include "sync/internal_api/syncapi_internal.h"
36 #include "sync/internal_api/syncapi_server_connection_manager.h"
37 #include "sync/js/js_arg_list.h"
38 #include "sync/js/js_event_details.h"
39 #include "sync/js/js_event_handler.h"
40 #include "sync/js/js_reply_handler.h"
41 #include "sync/notifier/invalidation_util.h"
42 #include "sync/notifier/invalidator.h"
43 #include "sync/protocol/proto_value_conversions.h"
44 #include "sync/protocol/sync.pb.h"
45 #include "sync/syncable/directory.h"
46 #include "sync/syncable/entry.h"
47 #include "sync/syncable/in_memory_directory_backing_store.h"
48 #include "sync/syncable/on_disk_directory_backing_store.h"
50 using base::TimeDelta;
51 using sync_pb::GetUpdatesCallerInfo;
55 using sessions::SyncSessionContext;
56 using syncable::ImmutableWriteTransactionInfo;
57 using syncable::SPECIFICS;
58 using syncable::UNIQUE_POSITION;
62 // Delays for syncer nudges.
63 static const int kDefaultNudgeDelayMilliseconds = 200;
64 static const int kPreferencesNudgeDelayMilliseconds = 2000;
65 static const int kSyncRefreshDelayMsec = 500;
66 static const int kSyncSchedulerDelayMsec = 250;
68 // Maximum count and size for traffic recorder.
69 static const unsigned int kMaxMessagesToRecord = 10;
70 static const unsigned int kMaxMessageSizeToRecord = 5 * 1024;
72 GetUpdatesCallerInfo::GetUpdatesSource GetSourceFromReason(
73 ConfigureReason reason) {
75 case CONFIGURE_REASON_RECONFIGURATION:
76 return GetUpdatesCallerInfo::RECONFIGURATION;
77 case CONFIGURE_REASON_MIGRATION:
78 return GetUpdatesCallerInfo::MIGRATION;
79 case CONFIGURE_REASON_NEW_CLIENT:
80 return GetUpdatesCallerInfo::NEW_CLIENT;
81 case CONFIGURE_REASON_NEWLY_ENABLED_DATA_TYPE:
82 case CONFIGURE_REASON_CRYPTO:
83 return GetUpdatesCallerInfo::NEWLY_SUPPORTED_DATATYPE;
87 return GetUpdatesCallerInfo::UNKNOWN;
92 // A class to calculate nudge delays for types.
95 static TimeDelta GetNudgeDelayTimeDelta(const ModelType& model_type,
96 SyncManagerImpl* core) {
97 NudgeDelayStrategy delay_type = GetNudgeDelayStrategy(model_type);
98 return GetNudgeDelayTimeDeltaFromType(delay_type,
104 // Possible types of nudge delay for datatypes.
105 // Note: These are just hints. If a sync happens then all dirty entries
106 // would be committed as part of the sync.
107 enum NudgeDelayStrategy {
111 // Sync this change while syncing another change.
114 // The datatype does not use one of the predefined wait times but defines
115 // its own wait time logic for nudge.
119 static NudgeDelayStrategy GetNudgeDelayStrategy(const ModelType& type) {
122 return ACCOMPANY_ONLY;
126 case FAVICON_TRACKING:
133 static TimeDelta GetNudgeDelayTimeDeltaFromType(
134 const NudgeDelayStrategy& delay_type, const ModelType& model_type,
135 const SyncManagerImpl* core) {
137 TimeDelta delay = TimeDelta::FromMilliseconds(
138 kDefaultNudgeDelayMilliseconds);
139 switch (delay_type) {
141 delay = TimeDelta::FromMilliseconds(
142 kDefaultNudgeDelayMilliseconds);
145 delay = TimeDelta::FromSeconds(kDefaultShortPollIntervalSeconds);
148 switch (model_type) {
150 delay = TimeDelta::FromMilliseconds(
151 kPreferencesNudgeDelayMilliseconds);
155 case FAVICON_TRACKING:
156 delay = core->scheduler()->GetSessionsCommitDelay();
169 SyncManagerImpl::SyncManagerImpl(const std::string& name)
171 change_delegate_(NULL),
173 observing_network_connectivity_changes_(false),
174 invalidator_state_(DEFAULT_INVALIDATION_ERROR),
175 traffic_recorder_(kMaxMessagesToRecord, kMaxMessageSizeToRecord),
177 report_unrecoverable_error_function_(NULL),
178 weak_ptr_factory_(this) {
179 // Pre-fill |notification_info_map_|.
180 for (int i = FIRST_REAL_MODEL_TYPE; i < MODEL_TYPE_COUNT; ++i) {
181 notification_info_map_.insert(
182 std::make_pair(ModelTypeFromInt(i), NotificationInfo()));
185 // Bind message handlers.
186 BindJsMessageHandler(
187 "getNotificationState",
188 &SyncManagerImpl::GetNotificationState);
189 BindJsMessageHandler(
190 "getNotificationInfo",
191 &SyncManagerImpl::GetNotificationInfo);
192 BindJsMessageHandler(
193 "getRootNodeDetails",
194 &SyncManagerImpl::GetRootNodeDetails);
195 BindJsMessageHandler(
196 "getNodeSummariesById",
197 &SyncManagerImpl::GetNodeSummariesById);
198 BindJsMessageHandler(
199 "getNodeDetailsById",
200 &SyncManagerImpl::GetNodeDetailsById);
201 BindJsMessageHandler(
203 &SyncManagerImpl::GetAllNodes);
204 BindJsMessageHandler(
206 &SyncManagerImpl::GetChildNodeIds);
207 BindJsMessageHandler(
208 "getClientServerTraffic",
209 &SyncManagerImpl::GetClientServerTraffic);
212 SyncManagerImpl::~SyncManagerImpl() {
213 DCHECK(thread_checker_.CalledOnValidThread());
214 CHECK(!initialized_);
217 SyncManagerImpl::NotificationInfo::NotificationInfo() : total_count(0) {}
218 SyncManagerImpl::NotificationInfo::~NotificationInfo() {}
220 base::DictionaryValue* SyncManagerImpl::NotificationInfo::ToValue() const {
221 base::DictionaryValue* value = new base::DictionaryValue();
222 value->SetInteger("totalCount", total_count);
223 value->SetString("payload", payload);
227 bool SyncManagerImpl::VisiblePositionsDiffer(
228 const syncable::EntryKernelMutation& mutation) const {
229 const syncable::EntryKernel& a = mutation.original;
230 const syncable::EntryKernel& b = mutation.mutated;
231 if (!b.ShouldMaintainPosition())
233 if (!a.ref(UNIQUE_POSITION).Equals(b.ref(UNIQUE_POSITION)))
235 if (a.ref(syncable::PARENT_ID) != b.ref(syncable::PARENT_ID))
240 bool SyncManagerImpl::VisiblePropertiesDiffer(
241 const syncable::EntryKernelMutation& mutation,
242 Cryptographer* cryptographer) const {
243 const syncable::EntryKernel& a = mutation.original;
244 const syncable::EntryKernel& b = mutation.mutated;
245 const sync_pb::EntitySpecifics& a_specifics = a.ref(SPECIFICS);
246 const sync_pb::EntitySpecifics& b_specifics = b.ref(SPECIFICS);
247 DCHECK_EQ(GetModelTypeFromSpecifics(a_specifics),
248 GetModelTypeFromSpecifics(b_specifics));
249 ModelType model_type = GetModelTypeFromSpecifics(b_specifics);
250 // Suppress updates to items that aren't tracked by any browser model.
251 if (model_type < FIRST_REAL_MODEL_TYPE ||
252 !a.ref(syncable::UNIQUE_SERVER_TAG).empty()) {
255 if (a.ref(syncable::IS_DIR) != b.ref(syncable::IS_DIR))
257 if (!AreSpecificsEqual(cryptographer,
258 a.ref(syncable::SPECIFICS),
259 b.ref(syncable::SPECIFICS))) {
262 // We only care if the name has changed if neither specifics is encrypted
263 // (encrypted nodes blow away the NON_UNIQUE_NAME).
264 if (!a_specifics.has_encrypted() && !b_specifics.has_encrypted() &&
265 a.ref(syncable::NON_UNIQUE_NAME) != b.ref(syncable::NON_UNIQUE_NAME))
267 if (VisiblePositionsDiffer(mutation))
272 void SyncManagerImpl::ThrowUnrecoverableError() {
273 DCHECK(thread_checker_.CalledOnValidThread());
274 ReadTransaction trans(FROM_HERE, GetUserShare());
275 trans.GetWrappedTrans()->OnUnrecoverableError(
276 FROM_HERE, "Simulating unrecoverable error for testing purposes.");
279 ModelTypeSet SyncManagerImpl::InitialSyncEndedTypes() {
280 return directory()->InitialSyncEndedTypes();
283 ModelTypeSet SyncManagerImpl::GetTypesWithEmptyProgressMarkerToken(
284 ModelTypeSet types) {
286 for (ModelTypeSet::Iterator i = types.First(); i.Good(); i.Inc()) {
287 sync_pb::DataTypeProgressMarker marker;
288 directory()->GetDownloadProgress(i.Get(), &marker);
290 if (marker.token().empty())
296 void SyncManagerImpl::ConfigureSyncer(
297 ConfigureReason reason,
298 ModelTypeSet to_download,
299 ModelTypeSet to_purge,
300 ModelTypeSet to_journal,
301 ModelTypeSet to_unapply,
302 const ModelSafeRoutingInfo& new_routing_info,
303 const base::Closure& ready_task,
304 const base::Closure& retry_task) {
305 DCHECK(thread_checker_.CalledOnValidThread());
306 DCHECK(!ready_task.is_null());
307 DCHECK(!retry_task.is_null());
309 DVLOG(1) << "Configuring -"
310 << "\n\t" << "current types: "
311 << ModelTypeSetToString(GetRoutingInfoTypes(new_routing_info))
312 << "\n\t" << "types to download: "
313 << ModelTypeSetToString(to_download)
314 << "\n\t" << "types to purge: "
315 << ModelTypeSetToString(to_purge)
316 << "\n\t" << "types to journal: "
317 << ModelTypeSetToString(to_journal)
318 << "\n\t" << "types to unapply: "
319 << ModelTypeSetToString(to_unapply);
320 if (!PurgeDisabledTypes(to_purge,
323 // We failed to cleanup the types. Invoke the ready task without actually
324 // configuring any types. The caller should detect this as a configuration
325 // failure and act appropriately.
330 ConfigurationParams params(GetSourceFromReason(reason),
335 scheduler_->Start(SyncScheduler::CONFIGURATION_MODE);
336 if (!scheduler_->ScheduleConfiguration(params))
340 void SyncManagerImpl::Init(
341 const base::FilePath& database_location,
342 const WeakHandle<JsEventHandler>& event_handler,
343 const std::string& sync_server_and_path,
346 scoped_ptr<HttpPostProviderFactory> post_factory,
347 const std::vector<ModelSafeWorker*>& workers,
348 ExtensionsActivity* extensions_activity,
349 SyncManager::ChangeDelegate* change_delegate,
350 const SyncCredentials& credentials,
351 const std::string& invalidator_client_id,
352 const std::string& restored_key_for_bootstrapping,
353 const std::string& restored_keystore_key_for_bootstrapping,
354 InternalComponentsFactory* internal_components_factory,
355 Encryptor* encryptor,
356 scoped_ptr<UnrecoverableErrorHandler> unrecoverable_error_handler,
357 ReportUnrecoverableErrorFunction report_unrecoverable_error_function,
358 CancelationSignal* cancelation_signal) {
359 CHECK(!initialized_);
360 DCHECK(thread_checker_.CalledOnValidThread());
361 DCHECK(post_factory.get());
362 DCHECK(!credentials.email.empty());
363 DCHECK(!credentials.sync_token.empty());
364 DCHECK(cancelation_signal);
365 DVLOG(1) << "SyncManager starting Init...";
367 weak_handle_this_ = MakeWeakHandle(weak_ptr_factory_.GetWeakPtr());
369 change_delegate_ = change_delegate;
371 AddObserver(&js_sync_manager_observer_);
372 SetJsEventHandler(event_handler);
374 AddObserver(&debug_info_event_listener_);
376 database_path_ = database_location.Append(
377 syncable::Directory::kSyncDatabaseFilename);
378 encryptor_ = encryptor;
379 unrecoverable_error_handler_ = unrecoverable_error_handler.Pass();
380 report_unrecoverable_error_function_ = report_unrecoverable_error_function;
382 allstatus_.SetHasKeystoreKey(
383 !restored_keystore_key_for_bootstrapping.empty());
384 sync_encryption_handler_.reset(new SyncEncryptionHandlerImpl(
387 restored_key_for_bootstrapping,
388 restored_keystore_key_for_bootstrapping));
389 sync_encryption_handler_->AddObserver(this);
390 sync_encryption_handler_->AddObserver(&debug_info_event_listener_);
391 sync_encryption_handler_->AddObserver(&js_sync_encryption_handler_observer_);
393 base::FilePath absolute_db_path = database_path_;
394 DCHECK(absolute_db_path.IsAbsolute());
396 scoped_ptr<syncable::DirectoryBackingStore> backing_store =
397 internal_components_factory->BuildDirectoryBackingStore(
398 credentials.email, absolute_db_path).Pass();
400 DCHECK(backing_store.get());
401 const std::string& username = credentials.email;
402 share_.directory.reset(
403 new syncable::Directory(
404 backing_store.release(),
405 unrecoverable_error_handler_.get(),
406 report_unrecoverable_error_function_,
407 sync_encryption_handler_.get(),
408 sync_encryption_handler_->GetCryptographerUnsafe()));
410 DVLOG(1) << "Username: " << username;
411 if (!OpenDirectory(username)) {
412 NotifyInitializationFailure();
413 LOG(ERROR) << "Sync manager initialization failed!";
417 connection_manager_.reset(new SyncAPIServerConnectionManager(
418 sync_server_and_path, port, use_ssl,
419 post_factory.release(), cancelation_signal));
420 connection_manager_->set_client_id(directory()->cache_guid());
421 connection_manager_->AddListener(this);
423 std::string sync_id = directory()->cache_guid();
425 allstatus_.SetSyncId(sync_id);
426 allstatus_.SetInvalidatorClientId(invalidator_client_id);
428 DVLOG(1) << "Setting sync client ID: " << sync_id;
429 DVLOG(1) << "Setting invalidator client ID: " << invalidator_client_id;
431 // Build a SyncSessionContext and store the worker in it.
432 DVLOG(1) << "Sync is bringing up SyncSessionContext.";
433 std::vector<SyncEngineEventListener*> listeners;
434 listeners.push_back(&allstatus_);
435 listeners.push_back(this);
436 session_context_ = internal_components_factory->BuildContext(
437 connection_manager_.get(),
442 &debug_info_event_listener_,
444 invalidator_client_id).Pass();
445 session_context_->set_account_name(credentials.email);
446 scheduler_ = internal_components_factory->BuildScheduler(
447 name_, session_context_.get(), cancelation_signal).Pass();
449 scheduler_->Start(SyncScheduler::CONFIGURATION_MODE);
453 net::NetworkChangeNotifier::AddIPAddressObserver(this);
454 net::NetworkChangeNotifier::AddConnectionTypeObserver(this);
455 observing_network_connectivity_changes_ = true;
457 UpdateCredentials(credentials);
459 NotifyInitializationSuccess();
462 void SyncManagerImpl::NotifyInitializationSuccess() {
463 FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
464 OnInitializationComplete(
465 MakeWeakHandle(weak_ptr_factory_.GetWeakPtr()),
466 MakeWeakHandle(debug_info_event_listener_.GetWeakPtr()),
467 true, InitialSyncEndedTypes()));
470 void SyncManagerImpl::NotifyInitializationFailure() {
471 FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
472 OnInitializationComplete(
473 MakeWeakHandle(weak_ptr_factory_.GetWeakPtr()),
474 MakeWeakHandle(debug_info_event_listener_.GetWeakPtr()),
475 false, ModelTypeSet()));
478 void SyncManagerImpl::OnPassphraseRequired(
479 PassphraseRequiredReason reason,
480 const sync_pb::EncryptedData& pending_keys) {
484 void SyncManagerImpl::OnPassphraseAccepted() {
488 void SyncManagerImpl::OnBootstrapTokenUpdated(
489 const std::string& bootstrap_token,
490 BootstrapTokenType type) {
491 if (type == KEYSTORE_BOOTSTRAP_TOKEN)
492 allstatus_.SetHasKeystoreKey(true);
495 void SyncManagerImpl::OnEncryptedTypesChanged(ModelTypeSet encrypted_types,
496 bool encrypt_everything) {
497 allstatus_.SetEncryptedTypes(encrypted_types);
500 void SyncManagerImpl::OnEncryptionComplete() {
504 void SyncManagerImpl::OnCryptographerStateChanged(
505 Cryptographer* cryptographer) {
506 allstatus_.SetCryptographerReady(cryptographer->is_ready());
507 allstatus_.SetCryptoHasPendingKeys(cryptographer->has_pending_keys());
508 allstatus_.SetKeystoreMigrationTime(
509 sync_encryption_handler_->migration_time());
512 void SyncManagerImpl::OnPassphraseTypeChanged(
514 base::Time explicit_passphrase_time) {
515 allstatus_.SetPassphraseType(type);
516 allstatus_.SetKeystoreMigrationTime(
517 sync_encryption_handler_->migration_time());
520 void SyncManagerImpl::StartSyncingNormally(
521 const ModelSafeRoutingInfo& routing_info) {
522 // Start the sync scheduler.
523 // TODO(sync): We always want the newest set of routes when we switch back
524 // to normal mode. Figure out how to enforce set_routing_info is always
525 // appropriately set and that it's only modified when switching to normal
527 DCHECK(thread_checker_.CalledOnValidThread());
528 session_context_->set_routing_info(routing_info);
529 scheduler_->Start(SyncScheduler::NORMAL_MODE);
532 syncable::Directory* SyncManagerImpl::directory() {
533 return share_.directory.get();
536 const SyncScheduler* SyncManagerImpl::scheduler() const {
537 return scheduler_.get();
540 bool SyncManagerImpl::GetHasInvalidAuthTokenForTest() const {
541 return connection_manager_->HasInvalidAuthToken();
544 bool SyncManagerImpl::OpenDirectory(const std::string& username) {
545 DCHECK(!initialized_) << "Should only happen once";
547 // Set before Open().
548 change_observer_ = MakeWeakHandle(js_mutation_event_observer_.AsWeakPtr());
549 WeakHandle<syncable::TransactionObserver> transaction_observer(
550 MakeWeakHandle(js_mutation_event_observer_.AsWeakPtr()));
552 syncable::DirOpenResult open_result = syncable::NOT_INITIALIZED;
553 open_result = directory()->Open(username, this, transaction_observer);
554 if (open_result != syncable::OPENED) {
555 LOG(ERROR) << "Could not open share for:" << username;
559 // Unapplied datatypes (those that do not have initial sync ended set) get
560 // re-downloaded during any configuration. But, it's possible for a datatype
561 // to have a progress marker but not have initial sync ended yet, making
562 // it a candidate for migration. This is a problem, as the DataTypeManager
563 // does not support a migration while it's already in the middle of a
564 // configuration. As a result, any partially synced datatype can stall the
565 // DTM, waiting for the configuration to complete, which it never will due
566 // to the migration error. In addition, a partially synced nigori will
567 // trigger the migration logic before the backend is initialized, resulting
568 // in crashes. We therefore detect and purge any partially synced types as
569 // part of initialization.
570 if (!PurgePartiallySyncedTypes())
576 bool SyncManagerImpl::PurgePartiallySyncedTypes() {
577 ModelTypeSet partially_synced_types = ModelTypeSet::All();
578 partially_synced_types.RemoveAll(InitialSyncEndedTypes());
579 partially_synced_types.RemoveAll(GetTypesWithEmptyProgressMarkerToken(
580 ModelTypeSet::All()));
582 DVLOG(1) << "Purging partially synced types "
583 << ModelTypeSetToString(partially_synced_types);
584 UMA_HISTOGRAM_COUNTS("Sync.PartiallySyncedTypes",
585 partially_synced_types.Size());
586 if (partially_synced_types.Empty())
588 return directory()->PurgeEntriesWithTypeIn(partially_synced_types,
593 bool SyncManagerImpl::PurgeDisabledTypes(
594 ModelTypeSet to_purge,
595 ModelTypeSet to_journal,
596 ModelTypeSet to_unapply) {
597 if (to_purge.Empty())
599 DVLOG(1) << "Purging disabled types " << ModelTypeSetToString(to_purge);
600 DCHECK(to_purge.HasAll(to_journal));
601 DCHECK(to_purge.HasAll(to_unapply));
602 return directory()->PurgeEntriesWithTypeIn(to_purge, to_journal, to_unapply);
605 void SyncManagerImpl::UpdateCredentials(const SyncCredentials& credentials) {
606 DCHECK(thread_checker_.CalledOnValidThread());
607 DCHECK(initialized_);
608 DCHECK(!credentials.email.empty());
609 DCHECK(!credentials.sync_token.empty());
611 observing_network_connectivity_changes_ = true;
612 if (!connection_manager_->SetAuthToken(credentials.sync_token))
613 return; // Auth token is known to be invalid, so exit early.
615 scheduler_->OnCredentialsUpdated();
617 // TODO(zea): pass the credential age to the debug info event listener.
620 void SyncManagerImpl::AddObserver(SyncManager::Observer* observer) {
621 DCHECK(thread_checker_.CalledOnValidThread());
622 observers_.AddObserver(observer);
625 void SyncManagerImpl::RemoveObserver(SyncManager::Observer* observer) {
626 DCHECK(thread_checker_.CalledOnValidThread());
627 observers_.RemoveObserver(observer);
630 void SyncManagerImpl::ShutdownOnSyncThread() {
631 DCHECK(thread_checker_.CalledOnValidThread());
633 // Prevent any in-flight method calls from running. Also
634 // invalidates |weak_handle_this_| and |change_observer_|.
635 weak_ptr_factory_.InvalidateWeakPtrs();
636 js_mutation_event_observer_.InvalidateWeakPtrs();
639 session_context_.reset();
641 if (sync_encryption_handler_) {
642 sync_encryption_handler_->RemoveObserver(&debug_info_event_listener_);
643 sync_encryption_handler_->RemoveObserver(this);
646 SetJsEventHandler(WeakHandle<JsEventHandler>());
647 RemoveObserver(&js_sync_manager_observer_);
649 RemoveObserver(&debug_info_event_listener_);
651 // |connection_manager_| may end up being NULL here in tests (in synchronous
652 // initialization mode).
654 // TODO(akalin): Fix this behavior.
655 if (connection_manager_)
656 connection_manager_->RemoveListener(this);
657 connection_manager_.reset();
659 net::NetworkChangeNotifier::RemoveIPAddressObserver(this);
660 net::NetworkChangeNotifier::RemoveConnectionTypeObserver(this);
661 observing_network_connectivity_changes_ = false;
663 if (initialized_ && directory()) {
664 directory()->SaveChanges();
667 share_.directory.reset();
669 change_delegate_ = NULL;
671 initialized_ = false;
673 // We reset these here, since only now we know they will not be
674 // accessed from other threads (since we shut down everything).
675 change_observer_.Reset();
676 weak_handle_this_.Reset();
679 void SyncManagerImpl::OnIPAddressChanged() {
680 if (!observing_network_connectivity_changes_) {
681 DVLOG(1) << "IP address change dropped.";
684 DVLOG(1) << "IP address change detected.";
685 OnNetworkConnectivityChangedImpl();
688 void SyncManagerImpl::OnConnectionTypeChanged(
689 net::NetworkChangeNotifier::ConnectionType) {
690 if (!observing_network_connectivity_changes_) {
691 DVLOG(1) << "Connection type change dropped.";
694 DVLOG(1) << "Connection type change detected.";
695 OnNetworkConnectivityChangedImpl();
698 void SyncManagerImpl::OnNetworkConnectivityChangedImpl() {
699 DCHECK(thread_checker_.CalledOnValidThread());
700 scheduler_->OnConnectionStatusChange();
703 void SyncManagerImpl::OnServerConnectionEvent(
704 const ServerConnectionEvent& event) {
705 DCHECK(thread_checker_.CalledOnValidThread());
706 if (event.connection_code ==
707 HttpResponse::SERVER_CONNECTION_OK) {
708 FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
709 OnConnectionStatusChange(CONNECTION_OK));
712 if (event.connection_code == HttpResponse::SYNC_AUTH_ERROR) {
713 observing_network_connectivity_changes_ = false;
714 FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
715 OnConnectionStatusChange(CONNECTION_AUTH_ERROR));
718 if (event.connection_code == HttpResponse::SYNC_SERVER_ERROR) {
719 FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
720 OnConnectionStatusChange(CONNECTION_SERVER_ERROR));
724 void SyncManagerImpl::HandleTransactionCompleteChangeEvent(
725 ModelTypeSet models_with_changes) {
726 // This notification happens immediately after the transaction mutex is
727 // released. This allows work to be performed without blocking other threads
728 // from acquiring a transaction.
729 if (!change_delegate_)
733 for (ModelTypeSet::Iterator it = models_with_changes.First();
734 it.Good(); it.Inc()) {
735 change_delegate_->OnChangesComplete(it.Get());
736 change_observer_.Call(
738 &SyncManager::ChangeObserver::OnChangesComplete,
744 SyncManagerImpl::HandleTransactionEndingChangeEvent(
745 const ImmutableWriteTransactionInfo& write_transaction_info,
746 syncable::BaseTransaction* trans) {
747 // This notification happens immediately before a syncable WriteTransaction
748 // falls out of scope. It happens while the channel mutex is still held,
749 // and while the transaction mutex is held, so it cannot be re-entrant.
750 if (!change_delegate_ || change_records_.empty())
751 return ModelTypeSet();
753 // This will continue the WriteTransaction using a read only wrapper.
754 // This is the last chance for read to occur in the WriteTransaction
755 // that's closing. This special ReadTransaction will not close the
756 // underlying transaction.
757 ReadTransaction read_trans(GetUserShare(), trans);
759 ModelTypeSet models_with_changes;
760 for (ChangeRecordMap::const_iterator it = change_records_.begin();
761 it != change_records_.end(); ++it) {
762 DCHECK(!it->second.Get().empty());
763 ModelType type = ModelTypeFromInt(it->first);
765 OnChangesApplied(type, trans->directory()->GetTransactionVersion(type),
766 &read_trans, it->second);
767 change_observer_.Call(FROM_HERE,
768 &SyncManager::ChangeObserver::OnChangesApplied,
769 type, write_transaction_info.Get().id, it->second);
770 models_with_changes.Put(type);
772 change_records_.clear();
773 return models_with_changes;
776 void SyncManagerImpl::HandleCalculateChangesChangeEventFromSyncApi(
777 const ImmutableWriteTransactionInfo& write_transaction_info,
778 syncable::BaseTransaction* trans,
779 std::vector<int64>* entries_changed) {
780 // We have been notified about a user action changing a sync model.
781 LOG_IF(WARNING, !change_records_.empty()) <<
782 "CALCULATE_CHANGES called with unapplied old changes.";
784 // The mutated model type, or UNSPECIFIED if nothing was mutated.
785 ModelTypeSet mutated_model_types;
787 const syncable::ImmutableEntryKernelMutationMap& mutations =
788 write_transaction_info.Get().mutations;
789 for (syncable::EntryKernelMutationMap::const_iterator it =
790 mutations.Get().begin(); it != mutations.Get().end(); ++it) {
791 if (!it->second.mutated.ref(syncable::IS_UNSYNCED)) {
795 ModelType model_type =
796 GetModelTypeFromSpecifics(it->second.mutated.ref(SPECIFICS));
797 if (model_type < FIRST_REAL_MODEL_TYPE) {
798 NOTREACHED() << "Permanent or underspecified item changed via syncapi.";
802 // Found real mutation.
803 if (model_type != UNSPECIFIED) {
804 mutated_model_types.Put(model_type);
805 entries_changed->push_back(it->second.mutated.ref(syncable::META_HANDLE));
809 // Nudge if necessary.
810 if (!mutated_model_types.Empty()) {
811 if (weak_handle_this_.IsInitialized()) {
812 weak_handle_this_.Call(FROM_HERE,
813 &SyncManagerImpl::RequestNudgeForDataTypes,
815 mutated_model_types);
822 void SyncManagerImpl::SetExtraChangeRecordData(int64 id,
823 ModelType type, ChangeReorderBuffer* buffer,
824 Cryptographer* cryptographer, const syncable::EntryKernel& original,
825 bool existed_before, bool exists_now) {
826 // If this is a deletion and the datatype was encrypted, we need to decrypt it
827 // and attach it to the buffer.
828 if (!exists_now && existed_before) {
829 sync_pb::EntitySpecifics original_specifics(original.ref(SPECIFICS));
830 if (type == PASSWORDS) {
831 // Passwords must use their own legacy ExtraPasswordChangeRecordData.
832 scoped_ptr<sync_pb::PasswordSpecificsData> data(
833 DecryptPasswordSpecifics(original_specifics, cryptographer));
838 buffer->SetExtraDataForId(id, new ExtraPasswordChangeRecordData(*data));
839 } else if (original_specifics.has_encrypted()) {
840 // All other datatypes can just create a new unencrypted specifics and
842 const sync_pb::EncryptedData& encrypted = original_specifics.encrypted();
843 if (!cryptographer->Decrypt(encrypted, &original_specifics)) {
848 buffer->SetSpecificsForId(id, original_specifics);
852 void SyncManagerImpl::HandleCalculateChangesChangeEventFromSyncer(
853 const ImmutableWriteTransactionInfo& write_transaction_info,
854 syncable::BaseTransaction* trans,
855 std::vector<int64>* entries_changed) {
856 // We only expect one notification per sync step, so change_buffers_ should
857 // contain no pending entries.
858 LOG_IF(WARNING, !change_records_.empty()) <<
859 "CALCULATE_CHANGES called with unapplied old changes.";
861 ChangeReorderBuffer change_buffers[MODEL_TYPE_COUNT];
863 Cryptographer* crypto = directory()->GetCryptographer(trans);
864 const syncable::ImmutableEntryKernelMutationMap& mutations =
865 write_transaction_info.Get().mutations;
866 for (syncable::EntryKernelMutationMap::const_iterator it =
867 mutations.Get().begin(); it != mutations.Get().end(); ++it) {
868 bool existed_before = !it->second.original.ref(syncable::IS_DEL);
869 bool exists_now = !it->second.mutated.ref(syncable::IS_DEL);
871 // Omit items that aren't associated with a model.
873 GetModelTypeFromSpecifics(it->second.mutated.ref(SPECIFICS));
874 if (type < FIRST_REAL_MODEL_TYPE)
877 int64 handle = it->first;
878 if (exists_now && !existed_before)
879 change_buffers[type].PushAddedItem(handle);
880 else if (!exists_now && existed_before)
881 change_buffers[type].PushDeletedItem(handle);
882 else if (exists_now && existed_before &&
883 VisiblePropertiesDiffer(it->second, crypto)) {
884 change_buffers[type].PushUpdatedItem(handle);
887 SetExtraChangeRecordData(handle, type, &change_buffers[type], crypto,
888 it->second.original, existed_before, exists_now);
891 ReadTransaction read_trans(GetUserShare(), trans);
892 for (int i = FIRST_REAL_MODEL_TYPE; i < MODEL_TYPE_COUNT; ++i) {
893 if (!change_buffers[i].IsEmpty()) {
894 if (change_buffers[i].GetAllChangesInTreeOrder(&read_trans,
895 &(change_records_[i]))) {
896 for (size_t j = 0; j < change_records_[i].Get().size(); ++j)
897 entries_changed->push_back((change_records_[i].Get())[j].id);
899 if (change_records_[i].Get().empty())
900 change_records_.erase(i);
905 TimeDelta SyncManagerImpl::GetNudgeDelayTimeDelta(
906 const ModelType& model_type) {
907 return NudgeStrategy::GetNudgeDelayTimeDelta(model_type, this);
910 void SyncManagerImpl::RequestNudgeForDataTypes(
911 const tracked_objects::Location& nudge_location,
912 ModelTypeSet types) {
913 debug_info_event_listener_.OnNudgeFromDatatype(types.First().Get());
915 // TODO(lipalani) : Calculate the nudge delay based on all types.
916 base::TimeDelta nudge_delay = NudgeStrategy::GetNudgeDelayTimeDelta(
919 allstatus_.IncrementNudgeCounter(NUDGE_SOURCE_LOCAL);
920 scheduler_->ScheduleLocalNudge(nudge_delay,
925 void SyncManagerImpl::OnSyncEngineEvent(const SyncEngineEvent& event) {
926 DCHECK(thread_checker_.CalledOnValidThread());
927 // Only send an event if this is due to a cycle ending and this cycle
928 // concludes a canonical "sync" process; that is, based on what is known
929 // locally we are "all happy" and up-to-date. There may be new changes on
930 // the server, but we'll get them on a subsequent sync.
932 // Notifications are sent at the end of every sync cycle, regardless of
933 // whether we should sync again.
934 if (event.what_happened == SyncEngineEvent::SYNC_CYCLE_ENDED) {
936 LOG(INFO) << "OnSyncCycleCompleted not sent because sync api is not "
941 DVLOG(1) << "Sending OnSyncCycleCompleted";
942 FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
943 OnSyncCycleCompleted(event.snapshot));
946 if (event.what_happened == SyncEngineEvent::STOP_SYNCING_PERMANENTLY) {
947 FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
948 OnStopSyncingPermanently());
952 if (event.what_happened == SyncEngineEvent::ACTIONABLE_ERROR) {
954 SyncManager::Observer, observers_,
956 event.snapshot.model_neutral_state().sync_protocol_error));
961 void SyncManagerImpl::SetJsEventHandler(
962 const WeakHandle<JsEventHandler>& event_handler) {
963 js_event_handler_ = event_handler;
964 js_sync_manager_observer_.SetJsEventHandler(js_event_handler_);
965 js_mutation_event_observer_.SetJsEventHandler(js_event_handler_);
966 js_sync_encryption_handler_observer_.SetJsEventHandler(js_event_handler_);
969 void SyncManagerImpl::ProcessJsMessage(
970 const std::string& name, const JsArgList& args,
971 const WeakHandle<JsReplyHandler>& reply_handler) {
977 if (!reply_handler.IsInitialized()) {
978 DVLOG(1) << "Uninitialized reply handler; dropping unknown message "
979 << name << " with args " << args.ToString();
983 JsMessageHandler js_message_handler = js_message_handlers_[name];
984 if (js_message_handler.is_null()) {
985 DVLOG(1) << "Dropping unknown message " << name
986 << " with args " << args.ToString();
990 reply_handler.Call(FROM_HERE,
991 &JsReplyHandler::HandleJsReply,
992 name, js_message_handler.Run(args));
995 void SyncManagerImpl::BindJsMessageHandler(
996 const std::string& name,
997 UnboundJsMessageHandler unbound_message_handler) {
998 js_message_handlers_[name] =
999 base::Bind(unbound_message_handler, base::Unretained(this));
1002 base::DictionaryValue* SyncManagerImpl::NotificationInfoToValue(
1003 const NotificationInfoMap& notification_info) {
1004 base::DictionaryValue* value = new base::DictionaryValue();
1006 for (NotificationInfoMap::const_iterator it = notification_info.begin();
1007 it != notification_info.end(); ++it) {
1008 const std::string model_type_str = ModelTypeToString(it->first);
1009 value->Set(model_type_str, it->second.ToValue());
1015 std::string SyncManagerImpl::NotificationInfoToString(
1016 const NotificationInfoMap& notification_info) {
1017 scoped_ptr<base::DictionaryValue> value(
1018 NotificationInfoToValue(notification_info));
1020 base::JSONWriter::Write(value.get(), &str);
1024 JsArgList SyncManagerImpl::GetNotificationState(
1025 const JsArgList& args) {
1026 const std::string& notification_state =
1027 InvalidatorStateToString(invalidator_state_);
1028 DVLOG(1) << "GetNotificationState: " << notification_state;
1029 base::ListValue return_args;
1030 return_args.Append(new base::StringValue(notification_state));
1031 return JsArgList(&return_args);
1034 JsArgList SyncManagerImpl::GetNotificationInfo(
1035 const JsArgList& args) {
1036 DVLOG(1) << "GetNotificationInfo: "
1037 << NotificationInfoToString(notification_info_map_);
1038 base::ListValue return_args;
1039 return_args.Append(NotificationInfoToValue(notification_info_map_));
1040 return JsArgList(&return_args);
1043 JsArgList SyncManagerImpl::GetRootNodeDetails(
1044 const JsArgList& args) {
1045 ReadTransaction trans(FROM_HERE, GetUserShare());
1046 ReadNode root(&trans);
1047 root.InitByRootLookup();
1048 base::ListValue return_args;
1049 return_args.Append(root.GetDetailsAsValue());
1050 return JsArgList(&return_args);
1053 JsArgList SyncManagerImpl::GetClientServerTraffic(
1054 const JsArgList& args) {
1055 base::ListValue return_args;
1056 base::ListValue* value = traffic_recorder_.ToValue();
1058 return_args.Append(value);
1059 return JsArgList(&return_args);
1064 int64 GetId(const base::ListValue& ids, int i) {
1066 if (!ids.GetString(i, &id_str)) {
1069 int64 id = kInvalidId;
1070 if (!base::StringToInt64(id_str, &id)) {
1076 JsArgList GetNodeInfoById(
1077 const JsArgList& args,
1078 UserShare* user_share,
1079 base::DictionaryValue* (BaseNode::*info_getter)() const) {
1081 base::ListValue return_args;
1082 base::ListValue* node_summaries = new base::ListValue();
1083 return_args.Append(node_summaries);
1084 const base::ListValue* id_list = NULL;
1085 ReadTransaction trans(FROM_HERE, user_share);
1086 if (args.Get().GetList(0, &id_list)) {
1088 for (size_t i = 0; i < id_list->GetSize(); ++i) {
1089 int64 id = GetId(*id_list, i);
1090 if (id == kInvalidId) {
1093 ReadNode node(&trans);
1094 if (node.InitByIdLookup(id) != BaseNode::INIT_OK) {
1097 node_summaries->Append((node.*info_getter)());
1100 return JsArgList(&return_args);
1105 JsArgList SyncManagerImpl::GetNodeSummariesById(const JsArgList& args) {
1106 return GetNodeInfoById(args, GetUserShare(), &BaseNode::GetSummaryAsValue);
1109 JsArgList SyncManagerImpl::GetNodeDetailsById(const JsArgList& args) {
1110 return GetNodeInfoById(args, GetUserShare(), &BaseNode::GetDetailsAsValue);
1113 JsArgList SyncManagerImpl::GetAllNodes(const JsArgList& args) {
1114 base::ListValue return_args;
1115 base::ListValue* result = new base::ListValue();
1116 return_args.Append(result);
1118 ReadTransaction trans(FROM_HERE, GetUserShare());
1119 std::vector<const syncable::EntryKernel*> entry_kernels;
1120 trans.GetDirectory()->GetAllEntryKernels(trans.GetWrappedTrans(),
1123 for (std::vector<const syncable::EntryKernel*>::const_iterator it =
1124 entry_kernels.begin(); it != entry_kernels.end(); ++it) {
1125 result->Append((*it)->ToValue(trans.GetCryptographer()));
1128 return JsArgList(&return_args);
1131 JsArgList SyncManagerImpl::GetChildNodeIds(const JsArgList& args) {
1132 base::ListValue return_args;
1133 base::ListValue* child_ids = new base::ListValue();
1134 return_args.Append(child_ids);
1135 int64 id = GetId(args.Get(), 0);
1136 if (id != kInvalidId) {
1137 ReadTransaction trans(FROM_HERE, GetUserShare());
1138 syncable::Directory::Metahandles child_handles;
1139 trans.GetDirectory()->GetChildHandlesByHandle(trans.GetWrappedTrans(),
1140 id, &child_handles);
1141 for (syncable::Directory::Metahandles::const_iterator it =
1142 child_handles.begin(); it != child_handles.end(); ++it) {
1143 child_ids->Append(new base::StringValue(base::Int64ToString(*it)));
1146 return JsArgList(&return_args);
1149 void SyncManagerImpl::UpdateNotificationInfo(
1150 const ObjectIdInvalidationMap& invalidation_map) {
1151 ObjectIdSet ids = invalidation_map.GetObjectIds();
1152 for (ObjectIdSet::const_iterator it = ids.begin(); it != ids.end(); ++it) {
1153 ModelType type = UNSPECIFIED;
1154 if (!ObjectIdToRealModelType(*it, &type)) {
1157 const SingleObjectInvalidationSet& type_invalidations =
1158 invalidation_map.ForObject(*it);
1159 for (SingleObjectInvalidationSet::const_iterator inv_it =
1160 type_invalidations.begin(); inv_it != type_invalidations.end();
1162 NotificationInfo* info = ¬ification_info_map_[type];
1163 info->total_count++;
1164 std::string payload =
1165 inv_it->is_unknown_version() ? "UNKNOWN" : inv_it->payload();
1166 info->payload = payload;
1171 void SyncManagerImpl::OnInvalidatorStateChange(InvalidatorState state) {
1172 DCHECK(thread_checker_.CalledOnValidThread());
1174 const std::string& state_str = InvalidatorStateToString(state);
1175 invalidator_state_ = state;
1176 DVLOG(1) << "Invalidator state changed to: " << state_str;
1177 const bool notifications_enabled =
1178 (invalidator_state_ == INVALIDATIONS_ENABLED);
1179 allstatus_.SetNotificationsEnabled(notifications_enabled);
1180 scheduler_->SetNotificationsEnabled(notifications_enabled);
1182 if (js_event_handler_.IsInitialized()) {
1183 base::DictionaryValue details;
1184 details.SetString("state", state_str);
1185 js_event_handler_.Call(FROM_HERE,
1186 &JsEventHandler::HandleJsEvent,
1187 "onNotificationStateChange",
1188 JsEventDetails(&details));
1192 void SyncManagerImpl::OnIncomingInvalidation(
1193 const ObjectIdInvalidationMap& invalidation_map) {
1194 DCHECK(thread_checker_.CalledOnValidThread());
1196 // We should never receive IDs from non-sync objects.
1197 ObjectIdSet ids = invalidation_map.GetObjectIds();
1198 for (ObjectIdSet::const_iterator it = ids.begin(); it != ids.end(); ++it) {
1200 if (!ObjectIdToRealModelType(*it, &type)) {
1201 DLOG(WARNING) << "Notification has invalid id: " << ObjectIdToString(*it);
1205 if (invalidation_map.Empty()) {
1206 LOG(WARNING) << "Sync received invalidation without any type information.";
1208 allstatus_.IncrementNudgeCounter(NUDGE_SOURCE_NOTIFICATION);
1209 scheduler_->ScheduleInvalidationNudge(
1210 TimeDelta::FromMilliseconds(kSyncSchedulerDelayMsec),
1211 invalidation_map, FROM_HERE);
1212 allstatus_.IncrementNotificationsReceived();
1213 UpdateNotificationInfo(invalidation_map);
1214 debug_info_event_listener_.OnIncomingNotification(invalidation_map);
1217 if (js_event_handler_.IsInitialized()) {
1218 base::DictionaryValue details;
1219 base::ListValue* changed_types = new base::ListValue();
1220 details.Set("changedTypes", changed_types);
1222 ObjectIdSet id_set = invalidation_map.GetObjectIds();
1223 ModelTypeSet nudged_types = ObjectIdSetToModelTypeSet(id_set);
1224 DCHECK(!nudged_types.Empty());
1225 for (ModelTypeSet::Iterator it = nudged_types.First();
1226 it.Good(); it.Inc()) {
1227 const std::string model_type_str = ModelTypeToString(it.Get());
1228 changed_types->Append(new base::StringValue(model_type_str));
1230 details.SetString("source", "REMOTE_INVALIDATION");
1231 js_event_handler_.Call(FROM_HERE,
1232 &JsEventHandler::HandleJsEvent,
1233 "onIncomingNotification",
1234 JsEventDetails(&details));
1238 void SyncManagerImpl::RefreshTypes(ModelTypeSet types) {
1239 DCHECK(thread_checker_.CalledOnValidThread());
1240 if (types.Empty()) {
1241 LOG(WARNING) << "Sync received refresh request with no types specified.";
1243 allstatus_.IncrementNudgeCounter(NUDGE_SOURCE_LOCAL_REFRESH);
1244 scheduler_->ScheduleLocalRefreshRequest(
1245 TimeDelta::FromMilliseconds(kSyncRefreshDelayMsec),
1249 if (js_event_handler_.IsInitialized()) {
1250 base::DictionaryValue details;
1251 base::ListValue* changed_types = new base::ListValue();
1252 details.Set("changedTypes", changed_types);
1253 for (ModelTypeSet::Iterator it = types.First(); it.Good(); it.Inc()) {
1254 const std::string& model_type_str =
1255 ModelTypeToString(it.Get());
1256 changed_types->Append(new base::StringValue(model_type_str));
1258 details.SetString("source", "LOCAL_INVALIDATION");
1259 js_event_handler_.Call(FROM_HERE,
1260 &JsEventHandler::HandleJsEvent,
1261 "onIncomingNotification",
1262 JsEventDetails(&details));
1266 SyncStatus SyncManagerImpl::GetDetailedStatus() const {
1267 return allstatus_.status();
1270 void SyncManagerImpl::SaveChanges() {
1271 directory()->SaveChanges();
1274 UserShare* SyncManagerImpl::GetUserShare() {
1275 DCHECK(initialized_);
1279 const std::string SyncManagerImpl::cache_guid() {
1280 DCHECK(initialized_);
1281 return directory()->cache_guid();
1284 bool SyncManagerImpl::ReceivedExperiment(Experiments* experiments) {
1285 ReadTransaction trans(FROM_HERE, GetUserShare());
1286 ReadNode nigori_node(&trans);
1287 if (nigori_node.InitByTagLookup(kNigoriTag) != BaseNode::INIT_OK) {
1288 DVLOG(1) << "Couldn't find Nigori node.";
1291 bool found_experiment = false;
1293 ReadNode autofill_culling_node(&trans);
1294 if (autofill_culling_node.InitByClientTagLookup(
1295 syncer::EXPERIMENTS,
1296 syncer::kAutofillCullingTag) == BaseNode::INIT_OK &&
1297 autofill_culling_node.GetExperimentsSpecifics().
1298 autofill_culling().enabled()) {
1299 experiments->autofill_culling = true;
1300 found_experiment = true;
1303 ReadNode favicon_sync_node(&trans);
1304 if (favicon_sync_node.InitByClientTagLookup(
1305 syncer::EXPERIMENTS,
1306 syncer::kFaviconSyncTag) == BaseNode::INIT_OK) {
1307 experiments->favicon_sync_limit =
1308 favicon_sync_node.GetExperimentsSpecifics().favicon_sync().
1309 favicon_sync_limit();
1310 found_experiment = true;
1313 ReadNode pre_commit_update_avoidance_node(&trans);
1314 if (pre_commit_update_avoidance_node.InitByClientTagLookup(
1315 syncer::EXPERIMENTS,
1316 syncer::kPreCommitUpdateAvoidanceTag) == BaseNode::INIT_OK) {
1317 session_context_->set_server_enabled_pre_commit_update_avoidance(
1318 pre_commit_update_avoidance_node.GetExperimentsSpecifics().
1319 pre_commit_update_avoidance().enabled());
1320 // We don't bother setting found_experiment. The frontend doesn't need to
1324 return found_experiment;
1327 bool SyncManagerImpl::HasUnsyncedItems() {
1328 ReadTransaction trans(FROM_HERE, GetUserShare());
1329 return (trans.GetWrappedTrans()->directory()->unsynced_entity_count() != 0);
1332 SyncEncryptionHandler* SyncManagerImpl::GetEncryptionHandler() {
1333 return sync_encryption_handler_.get();
1337 int SyncManagerImpl::GetDefaultNudgeDelay() {
1338 return kDefaultNudgeDelayMilliseconds;
1342 int SyncManagerImpl::GetPreferencesNudgeDelay() {
1343 return kPreferencesNudgeDelayMilliseconds;
1346 } // namespace syncer