• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "sync/internal_api/sync_manager_impl.h"
6 
7 #include <string>
8 
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/notifier/object_id_invalidation_map.h"
44 #include "sync/protocol/proto_value_conversions.h"
45 #include "sync/protocol/sync.pb.h"
46 #include "sync/syncable/directory.h"
47 #include "sync/syncable/entry.h"
48 #include "sync/syncable/in_memory_directory_backing_store.h"
49 #include "sync/syncable/on_disk_directory_backing_store.h"
50 
51 using base::TimeDelta;
52 using sync_pb::GetUpdatesCallerInfo;
53 
54 namespace syncer {
55 
56 using sessions::SyncSessionContext;
57 using syncable::ImmutableWriteTransactionInfo;
58 using syncable::SPECIFICS;
59 using syncable::UNIQUE_POSITION;
60 
61 namespace {
62 
63 // Delays for syncer nudges.
64 static const int kDefaultNudgeDelayMilliseconds = 200;
65 static const int kPreferencesNudgeDelayMilliseconds = 2000;
66 static const int kSyncRefreshDelayMsec = 500;
67 static const int kSyncSchedulerDelayMsec = 250;
68 
69 // Maximum count and size for traffic recorder.
70 static const unsigned int kMaxMessagesToRecord = 10;
71 static const unsigned int kMaxMessageSizeToRecord = 5 * 1024;
72 
GetSourceFromReason(ConfigureReason reason)73 GetUpdatesCallerInfo::GetUpdatesSource GetSourceFromReason(
74     ConfigureReason reason) {
75   switch (reason) {
76     case CONFIGURE_REASON_RECONFIGURATION:
77       return GetUpdatesCallerInfo::RECONFIGURATION;
78     case CONFIGURE_REASON_MIGRATION:
79       return GetUpdatesCallerInfo::MIGRATION;
80     case CONFIGURE_REASON_NEW_CLIENT:
81       return GetUpdatesCallerInfo::NEW_CLIENT;
82     case CONFIGURE_REASON_NEWLY_ENABLED_DATA_TYPE:
83     case CONFIGURE_REASON_CRYPTO:
84       return GetUpdatesCallerInfo::NEWLY_SUPPORTED_DATATYPE;
85     default:
86       NOTREACHED();
87   }
88   return GetUpdatesCallerInfo::UNKNOWN;
89 }
90 
91 }  // namespace
92 
93 // A class to calculate nudge delays for types.
94 class NudgeStrategy {
95  public:
GetNudgeDelayTimeDelta(const ModelType & model_type,SyncManagerImpl * core)96   static TimeDelta GetNudgeDelayTimeDelta(const ModelType& model_type,
97                                           SyncManagerImpl* core) {
98     NudgeDelayStrategy delay_type = GetNudgeDelayStrategy(model_type);
99     return GetNudgeDelayTimeDeltaFromType(delay_type,
100                                           model_type,
101                                           core);
102   }
103 
104  private:
105   // Possible types of nudge delay for datatypes.
106   // Note: These are just hints. If a sync happens then all dirty entries
107   // would be committed as part of the sync.
108   enum NudgeDelayStrategy {
109     // Sync right away.
110     IMMEDIATE,
111 
112     // Sync this change while syncing another change.
113     ACCOMPANY_ONLY,
114 
115     // The datatype does not use one of the predefined wait times but defines
116     // its own wait time logic for nudge.
117     CUSTOM,
118   };
119 
GetNudgeDelayStrategy(const ModelType & type)120   static NudgeDelayStrategy GetNudgeDelayStrategy(const ModelType& type) {
121     switch (type) {
122      case AUTOFILL:
123        return ACCOMPANY_ONLY;
124      case PREFERENCES:
125      case SESSIONS:
126      case FAVICON_IMAGES:
127      case FAVICON_TRACKING:
128        return CUSTOM;
129      default:
130        return IMMEDIATE;
131     }
132   }
133 
GetNudgeDelayTimeDeltaFromType(const NudgeDelayStrategy & delay_type,const ModelType & model_type,const SyncManagerImpl * core)134   static TimeDelta GetNudgeDelayTimeDeltaFromType(
135       const NudgeDelayStrategy& delay_type, const ModelType& model_type,
136       const SyncManagerImpl* core) {
137     CHECK(core);
138     TimeDelta delay = TimeDelta::FromMilliseconds(
139        kDefaultNudgeDelayMilliseconds);
140     switch (delay_type) {
141      case IMMEDIATE:
142        delay = TimeDelta::FromMilliseconds(
143            kDefaultNudgeDelayMilliseconds);
144        break;
145      case ACCOMPANY_ONLY:
146        delay = TimeDelta::FromSeconds(kDefaultShortPollIntervalSeconds);
147        break;
148      case CUSTOM:
149        switch (model_type) {
150          case PREFERENCES:
151            delay = TimeDelta::FromMilliseconds(
152                kPreferencesNudgeDelayMilliseconds);
153            break;
154          case SESSIONS:
155          case FAVICON_IMAGES:
156          case FAVICON_TRACKING:
157            delay = core->scheduler()->GetSessionsCommitDelay();
158            break;
159          default:
160            NOTREACHED();
161        }
162        break;
163      default:
164        NOTREACHED();
165     }
166     return delay;
167   }
168 };
169 
SyncManagerImpl(const std::string & name)170 SyncManagerImpl::SyncManagerImpl(const std::string& name)
171     : name_(name),
172       change_delegate_(NULL),
173       initialized_(false),
174       observing_network_connectivity_changes_(false),
175       invalidator_state_(DEFAULT_INVALIDATION_ERROR),
176       traffic_recorder_(kMaxMessagesToRecord, kMaxMessageSizeToRecord),
177       encryptor_(NULL),
178       report_unrecoverable_error_function_(NULL),
179       weak_ptr_factory_(this) {
180   // Pre-fill |notification_info_map_|.
181   for (int i = FIRST_REAL_MODEL_TYPE; i < MODEL_TYPE_COUNT; ++i) {
182     notification_info_map_.insert(
183         std::make_pair(ModelTypeFromInt(i), NotificationInfo()));
184   }
185 
186   // Bind message handlers.
187   BindJsMessageHandler(
188       "getNotificationState",
189       &SyncManagerImpl::GetNotificationState);
190   BindJsMessageHandler(
191       "getNotificationInfo",
192       &SyncManagerImpl::GetNotificationInfo);
193   BindJsMessageHandler(
194       "getRootNodeDetails",
195       &SyncManagerImpl::GetRootNodeDetails);
196   BindJsMessageHandler(
197       "getNodeSummariesById",
198       &SyncManagerImpl::GetNodeSummariesById);
199   BindJsMessageHandler(
200      "getNodeDetailsById",
201       &SyncManagerImpl::GetNodeDetailsById);
202   BindJsMessageHandler(
203       "getAllNodes",
204       &SyncManagerImpl::GetAllNodes);
205   BindJsMessageHandler(
206       "getChildNodeIds",
207       &SyncManagerImpl::GetChildNodeIds);
208   BindJsMessageHandler(
209       "getClientServerTraffic",
210       &SyncManagerImpl::GetClientServerTraffic);
211 }
212 
~SyncManagerImpl()213 SyncManagerImpl::~SyncManagerImpl() {
214   DCHECK(thread_checker_.CalledOnValidThread());
215   CHECK(!initialized_);
216 }
217 
NotificationInfo()218 SyncManagerImpl::NotificationInfo::NotificationInfo() : total_count(0) {}
~NotificationInfo()219 SyncManagerImpl::NotificationInfo::~NotificationInfo() {}
220 
ToValue() const221 base::DictionaryValue* SyncManagerImpl::NotificationInfo::ToValue() const {
222   base::DictionaryValue* value = new base::DictionaryValue();
223   value->SetInteger("totalCount", total_count);
224   value->SetString("payload", payload);
225   return value;
226 }
227 
VisiblePositionsDiffer(const syncable::EntryKernelMutation & mutation) const228 bool SyncManagerImpl::VisiblePositionsDiffer(
229     const syncable::EntryKernelMutation& mutation) const {
230   const syncable::EntryKernel& a = mutation.original;
231   const syncable::EntryKernel& b = mutation.mutated;
232   if (!b.ShouldMaintainPosition())
233     return false;
234   if (!a.ref(UNIQUE_POSITION).Equals(b.ref(UNIQUE_POSITION)))
235     return true;
236   if (a.ref(syncable::PARENT_ID) != b.ref(syncable::PARENT_ID))
237     return true;
238   return false;
239 }
240 
VisiblePropertiesDiffer(const syncable::EntryKernelMutation & mutation,Cryptographer * cryptographer) const241 bool SyncManagerImpl::VisiblePropertiesDiffer(
242     const syncable::EntryKernelMutation& mutation,
243     Cryptographer* cryptographer) const {
244   const syncable::EntryKernel& a = mutation.original;
245   const syncable::EntryKernel& b = mutation.mutated;
246   const sync_pb::EntitySpecifics& a_specifics = a.ref(SPECIFICS);
247   const sync_pb::EntitySpecifics& b_specifics = b.ref(SPECIFICS);
248   DCHECK_EQ(GetModelTypeFromSpecifics(a_specifics),
249             GetModelTypeFromSpecifics(b_specifics));
250   ModelType model_type = GetModelTypeFromSpecifics(b_specifics);
251   // Suppress updates to items that aren't tracked by any browser model.
252   if (model_type < FIRST_REAL_MODEL_TYPE ||
253       !a.ref(syncable::UNIQUE_SERVER_TAG).empty()) {
254     return false;
255   }
256   if (a.ref(syncable::IS_DIR) != b.ref(syncable::IS_DIR))
257     return true;
258   if (!AreSpecificsEqual(cryptographer,
259                          a.ref(syncable::SPECIFICS),
260                          b.ref(syncable::SPECIFICS))) {
261     return true;
262   }
263   // We only care if the name has changed if neither specifics is encrypted
264   // (encrypted nodes blow away the NON_UNIQUE_NAME).
265   if (!a_specifics.has_encrypted() && !b_specifics.has_encrypted() &&
266       a.ref(syncable::NON_UNIQUE_NAME) != b.ref(syncable::NON_UNIQUE_NAME))
267     return true;
268   if (VisiblePositionsDiffer(mutation))
269     return true;
270   return false;
271 }
272 
ThrowUnrecoverableError()273 void SyncManagerImpl::ThrowUnrecoverableError() {
274   DCHECK(thread_checker_.CalledOnValidThread());
275   ReadTransaction trans(FROM_HERE, GetUserShare());
276   trans.GetWrappedTrans()->OnUnrecoverableError(
277       FROM_HERE, "Simulating unrecoverable error for testing purposes.");
278 }
279 
InitialSyncEndedTypes()280 ModelTypeSet SyncManagerImpl::InitialSyncEndedTypes() {
281   return directory()->InitialSyncEndedTypes();
282 }
283 
GetTypesWithEmptyProgressMarkerToken(ModelTypeSet types)284 ModelTypeSet SyncManagerImpl::GetTypesWithEmptyProgressMarkerToken(
285     ModelTypeSet types) {
286   ModelTypeSet result;
287   for (ModelTypeSet::Iterator i = types.First(); i.Good(); i.Inc()) {
288     sync_pb::DataTypeProgressMarker marker;
289     directory()->GetDownloadProgress(i.Get(), &marker);
290 
291     if (marker.token().empty())
292       result.Put(i.Get());
293   }
294   return result;
295 }
296 
ConfigureSyncer(ConfigureReason reason,ModelTypeSet to_download,ModelTypeSet to_purge,ModelTypeSet to_journal,ModelTypeSet to_unapply,const ModelSafeRoutingInfo & new_routing_info,const base::Closure & ready_task,const base::Closure & retry_task)297 void SyncManagerImpl::ConfigureSyncer(
298     ConfigureReason reason,
299     ModelTypeSet to_download,
300     ModelTypeSet to_purge,
301     ModelTypeSet to_journal,
302     ModelTypeSet to_unapply,
303     const ModelSafeRoutingInfo& new_routing_info,
304     const base::Closure& ready_task,
305     const base::Closure& retry_task) {
306   DCHECK(thread_checker_.CalledOnValidThread());
307   DCHECK(!ready_task.is_null());
308   DCHECK(!retry_task.is_null());
309 
310   DVLOG(1) << "Configuring -"
311            << "\n\t" << "current types: "
312            << ModelTypeSetToString(GetRoutingInfoTypes(new_routing_info))
313            << "\n\t" << "types to download: "
314            << ModelTypeSetToString(to_download)
315            << "\n\t" << "types to purge: "
316            << ModelTypeSetToString(to_purge)
317            << "\n\t" << "types to journal: "
318            << ModelTypeSetToString(to_journal)
319            << "\n\t" << "types to unapply: "
320            << ModelTypeSetToString(to_unapply);
321   if (!PurgeDisabledTypes(to_purge,
322                           to_journal,
323                           to_unapply)) {
324     // We failed to cleanup the types. Invoke the ready task without actually
325     // configuring any types. The caller should detect this as a configuration
326     // failure and act appropriately.
327     ready_task.Run();
328     return;
329   }
330 
331   ConfigurationParams params(GetSourceFromReason(reason),
332                              to_download,
333                              new_routing_info,
334                              ready_task,
335                              retry_task);
336 
337   scheduler_->Start(SyncScheduler::CONFIGURATION_MODE);
338   scheduler_->ScheduleConfiguration(params);
339 }
340 
Init(const base::FilePath & database_location,const WeakHandle<JsEventHandler> & event_handler,const std::string & sync_server_and_path,int port,bool use_ssl,scoped_ptr<HttpPostProviderFactory> post_factory,const std::vector<ModelSafeWorker * > & workers,ExtensionsActivity * extensions_activity,SyncManager::ChangeDelegate * change_delegate,const SyncCredentials & credentials,const std::string & invalidator_client_id,const std::string & restored_key_for_bootstrapping,const std::string & restored_keystore_key_for_bootstrapping,InternalComponentsFactory * internal_components_factory,Encryptor * encryptor,scoped_ptr<UnrecoverableErrorHandler> unrecoverable_error_handler,ReportUnrecoverableErrorFunction report_unrecoverable_error_function,CancelationSignal * cancelation_signal)341 void SyncManagerImpl::Init(
342     const base::FilePath& database_location,
343     const WeakHandle<JsEventHandler>& event_handler,
344     const std::string& sync_server_and_path,
345     int port,
346     bool use_ssl,
347     scoped_ptr<HttpPostProviderFactory> post_factory,
348     const std::vector<ModelSafeWorker*>& workers,
349     ExtensionsActivity* extensions_activity,
350     SyncManager::ChangeDelegate* change_delegate,
351     const SyncCredentials& credentials,
352     const std::string& invalidator_client_id,
353     const std::string& restored_key_for_bootstrapping,
354     const std::string& restored_keystore_key_for_bootstrapping,
355     InternalComponentsFactory* internal_components_factory,
356     Encryptor* encryptor,
357     scoped_ptr<UnrecoverableErrorHandler> unrecoverable_error_handler,
358     ReportUnrecoverableErrorFunction report_unrecoverable_error_function,
359     CancelationSignal* cancelation_signal) {
360   CHECK(!initialized_);
361   DCHECK(thread_checker_.CalledOnValidThread());
362   DCHECK(post_factory.get());
363   DCHECK(!credentials.email.empty());
364   DCHECK(!credentials.sync_token.empty());
365   DCHECK(cancelation_signal);
366   DVLOG(1) << "SyncManager starting Init...";
367 
368   weak_handle_this_ = MakeWeakHandle(weak_ptr_factory_.GetWeakPtr());
369 
370   change_delegate_ = change_delegate;
371 
372   AddObserver(&js_sync_manager_observer_);
373   SetJsEventHandler(event_handler);
374 
375   AddObserver(&debug_info_event_listener_);
376 
377   database_path_ = database_location.Append(
378       syncable::Directory::kSyncDatabaseFilename);
379   encryptor_ = encryptor;
380   unrecoverable_error_handler_ = unrecoverable_error_handler.Pass();
381   report_unrecoverable_error_function_ = report_unrecoverable_error_function;
382 
383   allstatus_.SetHasKeystoreKey(
384       !restored_keystore_key_for_bootstrapping.empty());
385   sync_encryption_handler_.reset(new SyncEncryptionHandlerImpl(
386       &share_,
387       encryptor,
388       restored_key_for_bootstrapping,
389       restored_keystore_key_for_bootstrapping));
390   sync_encryption_handler_->AddObserver(this);
391   sync_encryption_handler_->AddObserver(&debug_info_event_listener_);
392   sync_encryption_handler_->AddObserver(&js_sync_encryption_handler_observer_);
393 
394   base::FilePath absolute_db_path = database_path_;
395   DCHECK(absolute_db_path.IsAbsolute());
396 
397   scoped_ptr<syncable::DirectoryBackingStore> backing_store =
398       internal_components_factory->BuildDirectoryBackingStore(
399           credentials.email, absolute_db_path).Pass();
400 
401   DCHECK(backing_store.get());
402   const std::string& username = credentials.email;
403   share_.directory.reset(
404       new syncable::Directory(
405           backing_store.release(),
406           unrecoverable_error_handler_.get(),
407           report_unrecoverable_error_function_,
408           sync_encryption_handler_.get(),
409           sync_encryption_handler_->GetCryptographerUnsafe()));
410 
411   DVLOG(1) << "Username: " << username;
412   if (!OpenDirectory(username)) {
413     NotifyInitializationFailure();
414     LOG(ERROR) << "Sync manager initialization failed!";
415     return;
416   }
417 
418   connection_manager_.reset(new SyncAPIServerConnectionManager(
419       sync_server_and_path, port, use_ssl,
420       post_factory.release(), cancelation_signal));
421   connection_manager_->set_client_id(directory()->cache_guid());
422   connection_manager_->AddListener(this);
423 
424   std::string sync_id = directory()->cache_guid();
425 
426   allstatus_.SetSyncId(sync_id);
427   allstatus_.SetInvalidatorClientId(invalidator_client_id);
428 
429   DVLOG(1) << "Setting sync client ID: " << sync_id;
430   DVLOG(1) << "Setting invalidator client ID: " << invalidator_client_id;
431 
432   // Build a SyncSessionContext and store the worker in it.
433   DVLOG(1) << "Sync is bringing up SyncSessionContext.";
434   std::vector<SyncEngineEventListener*> listeners;
435   listeners.push_back(&allstatus_);
436   listeners.push_back(this);
437   session_context_ = internal_components_factory->BuildContext(
438       connection_manager_.get(),
439       directory(),
440       workers,
441       extensions_activity,
442       listeners,
443       &debug_info_event_listener_,
444       &traffic_recorder_,
445       invalidator_client_id).Pass();
446   session_context_->set_account_name(credentials.email);
447   scheduler_ = internal_components_factory->BuildScheduler(
448       name_, session_context_.get(), cancelation_signal).Pass();
449 
450   scheduler_->Start(SyncScheduler::CONFIGURATION_MODE);
451 
452   initialized_ = true;
453 
454   net::NetworkChangeNotifier::AddIPAddressObserver(this);
455   net::NetworkChangeNotifier::AddConnectionTypeObserver(this);
456   observing_network_connectivity_changes_ = true;
457 
458   UpdateCredentials(credentials);
459 
460   NotifyInitializationSuccess();
461 }
462 
NotifyInitializationSuccess()463 void SyncManagerImpl::NotifyInitializationSuccess() {
464   FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
465                     OnInitializationComplete(
466                         MakeWeakHandle(weak_ptr_factory_.GetWeakPtr()),
467                         MakeWeakHandle(debug_info_event_listener_.GetWeakPtr()),
468                         true, InitialSyncEndedTypes()));
469 }
470 
NotifyInitializationFailure()471 void SyncManagerImpl::NotifyInitializationFailure() {
472   FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
473                     OnInitializationComplete(
474                         MakeWeakHandle(weak_ptr_factory_.GetWeakPtr()),
475                         MakeWeakHandle(debug_info_event_listener_.GetWeakPtr()),
476                         false, ModelTypeSet()));
477 }
478 
OnPassphraseRequired(PassphraseRequiredReason reason,const sync_pb::EncryptedData & pending_keys)479 void SyncManagerImpl::OnPassphraseRequired(
480     PassphraseRequiredReason reason,
481     const sync_pb::EncryptedData& pending_keys) {
482   // Does nothing.
483 }
484 
OnPassphraseAccepted()485 void SyncManagerImpl::OnPassphraseAccepted() {
486   // Does nothing.
487 }
488 
OnBootstrapTokenUpdated(const std::string & bootstrap_token,BootstrapTokenType type)489 void SyncManagerImpl::OnBootstrapTokenUpdated(
490     const std::string& bootstrap_token,
491     BootstrapTokenType type) {
492   if (type == KEYSTORE_BOOTSTRAP_TOKEN)
493     allstatus_.SetHasKeystoreKey(true);
494 }
495 
OnEncryptedTypesChanged(ModelTypeSet encrypted_types,bool encrypt_everything)496 void SyncManagerImpl::OnEncryptedTypesChanged(ModelTypeSet encrypted_types,
497                                               bool encrypt_everything) {
498   allstatus_.SetEncryptedTypes(encrypted_types);
499 }
500 
OnEncryptionComplete()501 void SyncManagerImpl::OnEncryptionComplete() {
502   // Does nothing.
503 }
504 
OnCryptographerStateChanged(Cryptographer * cryptographer)505 void SyncManagerImpl::OnCryptographerStateChanged(
506     Cryptographer* cryptographer) {
507   allstatus_.SetCryptographerReady(cryptographer->is_ready());
508   allstatus_.SetCryptoHasPendingKeys(cryptographer->has_pending_keys());
509   allstatus_.SetKeystoreMigrationTime(
510       sync_encryption_handler_->migration_time());
511 }
512 
OnPassphraseTypeChanged(PassphraseType type,base::Time explicit_passphrase_time)513 void SyncManagerImpl::OnPassphraseTypeChanged(
514     PassphraseType type,
515     base::Time explicit_passphrase_time) {
516   allstatus_.SetPassphraseType(type);
517   allstatus_.SetKeystoreMigrationTime(
518       sync_encryption_handler_->migration_time());
519 }
520 
StartSyncingNormally(const ModelSafeRoutingInfo & routing_info)521 void SyncManagerImpl::StartSyncingNormally(
522     const ModelSafeRoutingInfo& routing_info) {
523   // Start the sync scheduler.
524   // TODO(sync): We always want the newest set of routes when we switch back
525   // to normal mode. Figure out how to enforce set_routing_info is always
526   // appropriately set and that it's only modified when switching to normal
527   // mode.
528   DCHECK(thread_checker_.CalledOnValidThread());
529   session_context_->set_routing_info(routing_info);
530   scheduler_->Start(SyncScheduler::NORMAL_MODE);
531 }
532 
directory()533 syncable::Directory* SyncManagerImpl::directory() {
534   return share_.directory.get();
535 }
536 
scheduler() const537 const SyncScheduler* SyncManagerImpl::scheduler() const {
538   return scheduler_.get();
539 }
540 
GetHasInvalidAuthTokenForTest() const541 bool SyncManagerImpl::GetHasInvalidAuthTokenForTest() const {
542   return connection_manager_->HasInvalidAuthToken();
543 }
544 
OpenDirectory(const std::string & username)545 bool SyncManagerImpl::OpenDirectory(const std::string& username) {
546   DCHECK(!initialized_) << "Should only happen once";
547 
548   // Set before Open().
549   change_observer_ = MakeWeakHandle(js_mutation_event_observer_.AsWeakPtr());
550   WeakHandle<syncable::TransactionObserver> transaction_observer(
551       MakeWeakHandle(js_mutation_event_observer_.AsWeakPtr()));
552 
553   syncable::DirOpenResult open_result = syncable::NOT_INITIALIZED;
554   open_result = directory()->Open(username, this, transaction_observer);
555   if (open_result != syncable::OPENED) {
556     LOG(ERROR) << "Could not open share for:" << username;
557     return false;
558   }
559 
560   // Unapplied datatypes (those that do not have initial sync ended set) get
561   // re-downloaded during any configuration. But, it's possible for a datatype
562   // to have a progress marker but not have initial sync ended yet, making
563   // it a candidate for migration. This is a problem, as the DataTypeManager
564   // does not support a migration while it's already in the middle of a
565   // configuration. As a result, any partially synced datatype can stall the
566   // DTM, waiting for the configuration to complete, which it never will due
567   // to the migration error. In addition, a partially synced nigori will
568   // trigger the migration logic before the backend is initialized, resulting
569   // in crashes. We therefore detect and purge any partially synced types as
570   // part of initialization.
571   if (!PurgePartiallySyncedTypes())
572     return false;
573 
574   return true;
575 }
576 
PurgePartiallySyncedTypes()577 bool SyncManagerImpl::PurgePartiallySyncedTypes() {
578   ModelTypeSet partially_synced_types = ModelTypeSet::All();
579   partially_synced_types.RemoveAll(InitialSyncEndedTypes());
580   partially_synced_types.RemoveAll(GetTypesWithEmptyProgressMarkerToken(
581       ModelTypeSet::All()));
582 
583   DVLOG(1) << "Purging partially synced types "
584            << ModelTypeSetToString(partially_synced_types);
585   UMA_HISTOGRAM_COUNTS("Sync.PartiallySyncedTypes",
586                        partially_synced_types.Size());
587   if (partially_synced_types.Empty())
588     return true;
589   return directory()->PurgeEntriesWithTypeIn(partially_synced_types,
590                                              ModelTypeSet(),
591                                              ModelTypeSet());
592 }
593 
PurgeDisabledTypes(ModelTypeSet to_purge,ModelTypeSet to_journal,ModelTypeSet to_unapply)594 bool SyncManagerImpl::PurgeDisabledTypes(
595     ModelTypeSet to_purge,
596     ModelTypeSet to_journal,
597     ModelTypeSet to_unapply) {
598   if (to_purge.Empty())
599     return true;
600   DVLOG(1) << "Purging disabled types " << ModelTypeSetToString(to_purge);
601   DCHECK(to_purge.HasAll(to_journal));
602   DCHECK(to_purge.HasAll(to_unapply));
603   return directory()->PurgeEntriesWithTypeIn(to_purge, to_journal, to_unapply);
604 }
605 
UpdateCredentials(const SyncCredentials & credentials)606 void SyncManagerImpl::UpdateCredentials(const SyncCredentials& credentials) {
607   DCHECK(thread_checker_.CalledOnValidThread());
608   DCHECK(initialized_);
609   DCHECK(!credentials.email.empty());
610   DCHECK(!credentials.sync_token.empty());
611 
612   observing_network_connectivity_changes_ = true;
613   if (!connection_manager_->SetAuthToken(credentials.sync_token))
614     return;  // Auth token is known to be invalid, so exit early.
615 
616   scheduler_->OnCredentialsUpdated();
617 
618   // TODO(zea): pass the credential age to the debug info event listener.
619 }
620 
AddObserver(SyncManager::Observer * observer)621 void SyncManagerImpl::AddObserver(SyncManager::Observer* observer) {
622   DCHECK(thread_checker_.CalledOnValidThread());
623   observers_.AddObserver(observer);
624 }
625 
RemoveObserver(SyncManager::Observer * observer)626 void SyncManagerImpl::RemoveObserver(SyncManager::Observer* observer) {
627   DCHECK(thread_checker_.CalledOnValidThread());
628   observers_.RemoveObserver(observer);
629 }
630 
ShutdownOnSyncThread()631 void SyncManagerImpl::ShutdownOnSyncThread() {
632   DCHECK(thread_checker_.CalledOnValidThread());
633 
634   // Prevent any in-flight method calls from running.  Also
635   // invalidates |weak_handle_this_| and |change_observer_|.
636   weak_ptr_factory_.InvalidateWeakPtrs();
637   js_mutation_event_observer_.InvalidateWeakPtrs();
638 
639   scheduler_.reset();
640   session_context_.reset();
641 
642   if (sync_encryption_handler_) {
643     sync_encryption_handler_->RemoveObserver(&debug_info_event_listener_);
644     sync_encryption_handler_->RemoveObserver(this);
645   }
646 
647   SetJsEventHandler(WeakHandle<JsEventHandler>());
648   RemoveObserver(&js_sync_manager_observer_);
649 
650   RemoveObserver(&debug_info_event_listener_);
651 
652   // |connection_manager_| may end up being NULL here in tests (in synchronous
653   // initialization mode).
654   //
655   // TODO(akalin): Fix this behavior.
656   if (connection_manager_)
657     connection_manager_->RemoveListener(this);
658   connection_manager_.reset();
659 
660   net::NetworkChangeNotifier::RemoveIPAddressObserver(this);
661   net::NetworkChangeNotifier::RemoveConnectionTypeObserver(this);
662   observing_network_connectivity_changes_ = false;
663 
664   if (initialized_ && directory()) {
665     directory()->SaveChanges();
666   }
667 
668   share_.directory.reset();
669 
670   change_delegate_ = NULL;
671 
672   initialized_ = false;
673 
674   // We reset these here, since only now we know they will not be
675   // accessed from other threads (since we shut down everything).
676   change_observer_.Reset();
677   weak_handle_this_.Reset();
678 }
679 
OnIPAddressChanged()680 void SyncManagerImpl::OnIPAddressChanged() {
681   if (!observing_network_connectivity_changes_) {
682     DVLOG(1) << "IP address change dropped.";
683     return;
684   }
685   DVLOG(1) << "IP address change detected.";
686   OnNetworkConnectivityChangedImpl();
687 }
688 
OnConnectionTypeChanged(net::NetworkChangeNotifier::ConnectionType)689 void SyncManagerImpl::OnConnectionTypeChanged(
690   net::NetworkChangeNotifier::ConnectionType) {
691   if (!observing_network_connectivity_changes_) {
692     DVLOG(1) << "Connection type change dropped.";
693     return;
694   }
695   DVLOG(1) << "Connection type change detected.";
696   OnNetworkConnectivityChangedImpl();
697 }
698 
OnNetworkConnectivityChangedImpl()699 void SyncManagerImpl::OnNetworkConnectivityChangedImpl() {
700   DCHECK(thread_checker_.CalledOnValidThread());
701   scheduler_->OnConnectionStatusChange();
702 }
703 
OnServerConnectionEvent(const ServerConnectionEvent & event)704 void SyncManagerImpl::OnServerConnectionEvent(
705     const ServerConnectionEvent& event) {
706   DCHECK(thread_checker_.CalledOnValidThread());
707   if (event.connection_code ==
708       HttpResponse::SERVER_CONNECTION_OK) {
709     FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
710                       OnConnectionStatusChange(CONNECTION_OK));
711   }
712 
713   if (event.connection_code == HttpResponse::SYNC_AUTH_ERROR) {
714     observing_network_connectivity_changes_ = false;
715     FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
716                       OnConnectionStatusChange(CONNECTION_AUTH_ERROR));
717   }
718 
719   if (event.connection_code == HttpResponse::SYNC_SERVER_ERROR) {
720     FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
721                       OnConnectionStatusChange(CONNECTION_SERVER_ERROR));
722   }
723 }
724 
HandleTransactionCompleteChangeEvent(ModelTypeSet models_with_changes)725 void SyncManagerImpl::HandleTransactionCompleteChangeEvent(
726     ModelTypeSet models_with_changes) {
727   // This notification happens immediately after the transaction mutex is
728   // released. This allows work to be performed without blocking other threads
729   // from acquiring a transaction.
730   if (!change_delegate_)
731     return;
732 
733   // Call commit.
734   for (ModelTypeSet::Iterator it = models_with_changes.First();
735        it.Good(); it.Inc()) {
736     change_delegate_->OnChangesComplete(it.Get());
737     change_observer_.Call(
738         FROM_HERE,
739         &SyncManager::ChangeObserver::OnChangesComplete,
740         it.Get());
741   }
742 }
743 
744 ModelTypeSet
HandleTransactionEndingChangeEvent(const ImmutableWriteTransactionInfo & write_transaction_info,syncable::BaseTransaction * trans)745 SyncManagerImpl::HandleTransactionEndingChangeEvent(
746     const ImmutableWriteTransactionInfo& write_transaction_info,
747     syncable::BaseTransaction* trans) {
748   // This notification happens immediately before a syncable WriteTransaction
749   // falls out of scope. It happens while the channel mutex is still held,
750   // and while the transaction mutex is held, so it cannot be re-entrant.
751   if (!change_delegate_ || change_records_.empty())
752     return ModelTypeSet();
753 
754   // This will continue the WriteTransaction using a read only wrapper.
755   // This is the last chance for read to occur in the WriteTransaction
756   // that's closing. This special ReadTransaction will not close the
757   // underlying transaction.
758   ReadTransaction read_trans(GetUserShare(), trans);
759 
760   ModelTypeSet models_with_changes;
761   for (ChangeRecordMap::const_iterator it = change_records_.begin();
762       it != change_records_.end(); ++it) {
763     DCHECK(!it->second.Get().empty());
764     ModelType type = ModelTypeFromInt(it->first);
765     change_delegate_->
766         OnChangesApplied(type, trans->directory()->GetTransactionVersion(type),
767                          &read_trans, it->second);
768     change_observer_.Call(FROM_HERE,
769         &SyncManager::ChangeObserver::OnChangesApplied,
770         type, write_transaction_info.Get().id, it->second);
771     models_with_changes.Put(type);
772   }
773   change_records_.clear();
774   return models_with_changes;
775 }
776 
HandleCalculateChangesChangeEventFromSyncApi(const ImmutableWriteTransactionInfo & write_transaction_info,syncable::BaseTransaction * trans,std::vector<int64> * entries_changed)777 void SyncManagerImpl::HandleCalculateChangesChangeEventFromSyncApi(
778     const ImmutableWriteTransactionInfo& write_transaction_info,
779     syncable::BaseTransaction* trans,
780     std::vector<int64>* entries_changed) {
781   // We have been notified about a user action changing a sync model.
782   LOG_IF(WARNING, !change_records_.empty()) <<
783       "CALCULATE_CHANGES called with unapplied old changes.";
784 
785   // The mutated model type, or UNSPECIFIED if nothing was mutated.
786   ModelTypeSet mutated_model_types;
787 
788   const syncable::ImmutableEntryKernelMutationMap& mutations =
789       write_transaction_info.Get().mutations;
790   for (syncable::EntryKernelMutationMap::const_iterator it =
791            mutations.Get().begin(); it != mutations.Get().end(); ++it) {
792     if (!it->second.mutated.ref(syncable::IS_UNSYNCED)) {
793       continue;
794     }
795 
796     ModelType model_type =
797         GetModelTypeFromSpecifics(it->second.mutated.ref(SPECIFICS));
798     if (model_type < FIRST_REAL_MODEL_TYPE) {
799       NOTREACHED() << "Permanent or underspecified item changed via syncapi.";
800       continue;
801     }
802 
803     // Found real mutation.
804     if (model_type != UNSPECIFIED) {
805       mutated_model_types.Put(model_type);
806       entries_changed->push_back(it->second.mutated.ref(syncable::META_HANDLE));
807     }
808   }
809 
810   // Nudge if necessary.
811   if (!mutated_model_types.Empty()) {
812     if (weak_handle_this_.IsInitialized()) {
813       weak_handle_this_.Call(FROM_HERE,
814                              &SyncManagerImpl::RequestNudgeForDataTypes,
815                              FROM_HERE,
816                              mutated_model_types);
817     } else {
818       NOTREACHED();
819     }
820   }
821 }
822 
SetExtraChangeRecordData(int64 id,ModelType type,ChangeReorderBuffer * buffer,Cryptographer * cryptographer,const syncable::EntryKernel & original,bool existed_before,bool exists_now)823 void SyncManagerImpl::SetExtraChangeRecordData(int64 id,
824     ModelType type, ChangeReorderBuffer* buffer,
825     Cryptographer* cryptographer, const syncable::EntryKernel& original,
826     bool existed_before, bool exists_now) {
827   // If this is a deletion and the datatype was encrypted, we need to decrypt it
828   // and attach it to the buffer.
829   if (!exists_now && existed_before) {
830     sync_pb::EntitySpecifics original_specifics(original.ref(SPECIFICS));
831     if (type == PASSWORDS) {
832       // Passwords must use their own legacy ExtraPasswordChangeRecordData.
833       scoped_ptr<sync_pb::PasswordSpecificsData> data(
834           DecryptPasswordSpecifics(original_specifics, cryptographer));
835       if (!data) {
836         NOTREACHED();
837         return;
838       }
839       buffer->SetExtraDataForId(id, new ExtraPasswordChangeRecordData(*data));
840     } else if (original_specifics.has_encrypted()) {
841       // All other datatypes can just create a new unencrypted specifics and
842       // attach it.
843       const sync_pb::EncryptedData& encrypted = original_specifics.encrypted();
844       if (!cryptographer->Decrypt(encrypted, &original_specifics)) {
845         NOTREACHED();
846         return;
847       }
848     }
849     buffer->SetSpecificsForId(id, original_specifics);
850   }
851 }
852 
HandleCalculateChangesChangeEventFromSyncer(const ImmutableWriteTransactionInfo & write_transaction_info,syncable::BaseTransaction * trans,std::vector<int64> * entries_changed)853 void SyncManagerImpl::HandleCalculateChangesChangeEventFromSyncer(
854     const ImmutableWriteTransactionInfo& write_transaction_info,
855     syncable::BaseTransaction* trans,
856     std::vector<int64>* entries_changed) {
857   // We only expect one notification per sync step, so change_buffers_ should
858   // contain no pending entries.
859   LOG_IF(WARNING, !change_records_.empty()) <<
860       "CALCULATE_CHANGES called with unapplied old changes.";
861 
862   ChangeReorderBuffer change_buffers[MODEL_TYPE_COUNT];
863 
864   Cryptographer* crypto = directory()->GetCryptographer(trans);
865   const syncable::ImmutableEntryKernelMutationMap& mutations =
866       write_transaction_info.Get().mutations;
867   for (syncable::EntryKernelMutationMap::const_iterator it =
868            mutations.Get().begin(); it != mutations.Get().end(); ++it) {
869     bool existed_before = !it->second.original.ref(syncable::IS_DEL);
870     bool exists_now = !it->second.mutated.ref(syncable::IS_DEL);
871 
872     // Omit items that aren't associated with a model.
873     ModelType type =
874         GetModelTypeFromSpecifics(it->second.mutated.ref(SPECIFICS));
875     if (type < FIRST_REAL_MODEL_TYPE)
876       continue;
877 
878     int64 handle = it->first;
879     if (exists_now && !existed_before)
880       change_buffers[type].PushAddedItem(handle);
881     else if (!exists_now && existed_before)
882       change_buffers[type].PushDeletedItem(handle);
883     else if (exists_now && existed_before &&
884              VisiblePropertiesDiffer(it->second, crypto)) {
885       change_buffers[type].PushUpdatedItem(handle);
886     }
887 
888     SetExtraChangeRecordData(handle, type, &change_buffers[type], crypto,
889                              it->second.original, existed_before, exists_now);
890   }
891 
892   ReadTransaction read_trans(GetUserShare(), trans);
893   for (int i = FIRST_REAL_MODEL_TYPE; i < MODEL_TYPE_COUNT; ++i) {
894     if (!change_buffers[i].IsEmpty()) {
895       if (change_buffers[i].GetAllChangesInTreeOrder(&read_trans,
896                                                      &(change_records_[i]))) {
897         for (size_t j = 0; j < change_records_[i].Get().size(); ++j)
898           entries_changed->push_back((change_records_[i].Get())[j].id);
899       }
900       if (change_records_[i].Get().empty())
901         change_records_.erase(i);
902     }
903   }
904 }
905 
GetNudgeDelayTimeDelta(const ModelType & model_type)906 TimeDelta SyncManagerImpl::GetNudgeDelayTimeDelta(
907     const ModelType& model_type) {
908   return NudgeStrategy::GetNudgeDelayTimeDelta(model_type, this);
909 }
910 
RequestNudgeForDataTypes(const tracked_objects::Location & nudge_location,ModelTypeSet types)911 void SyncManagerImpl::RequestNudgeForDataTypes(
912     const tracked_objects::Location& nudge_location,
913     ModelTypeSet types) {
914   debug_info_event_listener_.OnNudgeFromDatatype(types.First().Get());
915 
916   // TODO(lipalani) : Calculate the nudge delay based on all types.
917   base::TimeDelta nudge_delay = NudgeStrategy::GetNudgeDelayTimeDelta(
918       types.First().Get(),
919       this);
920   allstatus_.IncrementNudgeCounter(NUDGE_SOURCE_LOCAL);
921   scheduler_->ScheduleLocalNudge(nudge_delay,
922                                  types,
923                                  nudge_location);
924 }
925 
OnSyncEngineEvent(const SyncEngineEvent & event)926 void SyncManagerImpl::OnSyncEngineEvent(const SyncEngineEvent& event) {
927   DCHECK(thread_checker_.CalledOnValidThread());
928   // Only send an event if this is due to a cycle ending and this cycle
929   // concludes a canonical "sync" process; that is, based on what is known
930   // locally we are "all happy" and up-to-date.  There may be new changes on
931   // the server, but we'll get them on a subsequent sync.
932   //
933   // Notifications are sent at the end of every sync cycle, regardless of
934   // whether we should sync again.
935   if (event.what_happened == SyncEngineEvent::SYNC_CYCLE_ENDED) {
936     if (!initialized_) {
937       DVLOG(1) << "OnSyncCycleCompleted not sent because sync api is not "
938                << "initialized";
939       return;
940     }
941 
942     DVLOG(1) << "Sending OnSyncCycleCompleted";
943     FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
944                       OnSyncCycleCompleted(event.snapshot));
945   }
946 
947   if (event.what_happened == SyncEngineEvent::STOP_SYNCING_PERMANENTLY) {
948     FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
949                       OnStopSyncingPermanently());
950     return;
951   }
952 
953   if (event.what_happened == SyncEngineEvent::ACTIONABLE_ERROR) {
954     FOR_EACH_OBSERVER(
955         SyncManager::Observer, observers_,
956         OnActionableError(
957             event.snapshot.model_neutral_state().sync_protocol_error));
958     return;
959   }
960 }
961 
SetJsEventHandler(const WeakHandle<JsEventHandler> & event_handler)962 void SyncManagerImpl::SetJsEventHandler(
963     const WeakHandle<JsEventHandler>& event_handler) {
964   js_event_handler_ = event_handler;
965   js_sync_manager_observer_.SetJsEventHandler(js_event_handler_);
966   js_mutation_event_observer_.SetJsEventHandler(js_event_handler_);
967   js_sync_encryption_handler_observer_.SetJsEventHandler(js_event_handler_);
968 }
969 
ProcessJsMessage(const std::string & name,const JsArgList & args,const WeakHandle<JsReplyHandler> & reply_handler)970 void SyncManagerImpl::ProcessJsMessage(
971     const std::string& name, const JsArgList& args,
972     const WeakHandle<JsReplyHandler>& reply_handler) {
973   if (!initialized_) {
974     NOTREACHED();
975     return;
976   }
977 
978   if (!reply_handler.IsInitialized()) {
979     DVLOG(1) << "Uninitialized reply handler; dropping unknown message "
980             << name << " with args " << args.ToString();
981     return;
982   }
983 
984   JsMessageHandler js_message_handler = js_message_handlers_[name];
985   if (js_message_handler.is_null()) {
986     DVLOG(1) << "Dropping unknown message " << name
987              << " with args " << args.ToString();
988     return;
989   }
990 
991   reply_handler.Call(FROM_HERE,
992                      &JsReplyHandler::HandleJsReply,
993                      name, js_message_handler.Run(args));
994 }
995 
BindJsMessageHandler(const std::string & name,UnboundJsMessageHandler unbound_message_handler)996 void SyncManagerImpl::BindJsMessageHandler(
997     const std::string& name,
998     UnboundJsMessageHandler unbound_message_handler) {
999   js_message_handlers_[name] =
1000       base::Bind(unbound_message_handler, base::Unretained(this));
1001 }
1002 
NotificationInfoToValue(const NotificationInfoMap & notification_info)1003 base::DictionaryValue* SyncManagerImpl::NotificationInfoToValue(
1004     const NotificationInfoMap& notification_info) {
1005   base::DictionaryValue* value = new base::DictionaryValue();
1006 
1007   for (NotificationInfoMap::const_iterator it = notification_info.begin();
1008       it != notification_info.end(); ++it) {
1009     const std::string model_type_str = ModelTypeToString(it->first);
1010     value->Set(model_type_str, it->second.ToValue());
1011   }
1012 
1013   return value;
1014 }
1015 
NotificationInfoToString(const NotificationInfoMap & notification_info)1016 std::string SyncManagerImpl::NotificationInfoToString(
1017     const NotificationInfoMap& notification_info) {
1018   scoped_ptr<base::DictionaryValue> value(
1019       NotificationInfoToValue(notification_info));
1020   std::string str;
1021   base::JSONWriter::Write(value.get(), &str);
1022   return str;
1023 }
1024 
GetNotificationState(const JsArgList & args)1025 JsArgList SyncManagerImpl::GetNotificationState(
1026     const JsArgList& args) {
1027   const std::string& notification_state =
1028       InvalidatorStateToString(invalidator_state_);
1029   DVLOG(1) << "GetNotificationState: " << notification_state;
1030   base::ListValue return_args;
1031   return_args.Append(new base::StringValue(notification_state));
1032   return JsArgList(&return_args);
1033 }
1034 
GetNotificationInfo(const JsArgList & args)1035 JsArgList SyncManagerImpl::GetNotificationInfo(
1036     const JsArgList& args) {
1037   DVLOG(1) << "GetNotificationInfo: "
1038            << NotificationInfoToString(notification_info_map_);
1039   base::ListValue return_args;
1040   return_args.Append(NotificationInfoToValue(notification_info_map_));
1041   return JsArgList(&return_args);
1042 }
1043 
GetRootNodeDetails(const JsArgList & args)1044 JsArgList SyncManagerImpl::GetRootNodeDetails(
1045     const JsArgList& args) {
1046   ReadTransaction trans(FROM_HERE, GetUserShare());
1047   ReadNode root(&trans);
1048   root.InitByRootLookup();
1049   base::ListValue return_args;
1050   return_args.Append(root.GetDetailsAsValue());
1051   return JsArgList(&return_args);
1052 }
1053 
GetClientServerTraffic(const JsArgList & args)1054 JsArgList SyncManagerImpl::GetClientServerTraffic(
1055     const JsArgList& args) {
1056   base::ListValue return_args;
1057   base::ListValue* value = traffic_recorder_.ToValue();
1058   if (value != NULL)
1059     return_args.Append(value);
1060   return JsArgList(&return_args);
1061 }
1062 
1063 namespace {
1064 
GetId(const base::ListValue & ids,int i)1065 int64 GetId(const base::ListValue& ids, int i) {
1066   std::string id_str;
1067   if (!ids.GetString(i, &id_str)) {
1068     return kInvalidId;
1069   }
1070   int64 id = kInvalidId;
1071   if (!base::StringToInt64(id_str, &id)) {
1072     return kInvalidId;
1073   }
1074   return id;
1075 }
1076 
GetNodeInfoById(const JsArgList & args,UserShare * user_share,base::DictionaryValue * (BaseNode::* info_getter)()const)1077 JsArgList GetNodeInfoById(
1078     const JsArgList& args,
1079     UserShare* user_share,
1080     base::DictionaryValue* (BaseNode::*info_getter)() const) {
1081   CHECK(info_getter);
1082   base::ListValue return_args;
1083   base::ListValue* node_summaries = new base::ListValue();
1084   return_args.Append(node_summaries);
1085   const base::ListValue* id_list = NULL;
1086   ReadTransaction trans(FROM_HERE, user_share);
1087   if (args.Get().GetList(0, &id_list)) {
1088     CHECK(id_list);
1089     for (size_t i = 0; i < id_list->GetSize(); ++i) {
1090       int64 id = GetId(*id_list, i);
1091       if (id == kInvalidId) {
1092         continue;
1093       }
1094       ReadNode node(&trans);
1095       if (node.InitByIdLookup(id) != BaseNode::INIT_OK) {
1096         continue;
1097       }
1098       node_summaries->Append((node.*info_getter)());
1099     }
1100   }
1101   return JsArgList(&return_args);
1102 }
1103 
1104 }  // namespace
1105 
GetNodeSummariesById(const JsArgList & args)1106 JsArgList SyncManagerImpl::GetNodeSummariesById(const JsArgList& args) {
1107   return GetNodeInfoById(args, GetUserShare(), &BaseNode::GetSummaryAsValue);
1108 }
1109 
GetNodeDetailsById(const JsArgList & args)1110 JsArgList SyncManagerImpl::GetNodeDetailsById(const JsArgList& args) {
1111   return GetNodeInfoById(args, GetUserShare(), &BaseNode::GetDetailsAsValue);
1112 }
1113 
GetAllNodes(const JsArgList & args)1114 JsArgList SyncManagerImpl::GetAllNodes(const JsArgList& args) {
1115   base::ListValue return_args;
1116   base::ListValue* result = new base::ListValue();
1117   return_args.Append(result);
1118 
1119   ReadTransaction trans(FROM_HERE, GetUserShare());
1120   std::vector<const syncable::EntryKernel*> entry_kernels;
1121   trans.GetDirectory()->GetAllEntryKernels(trans.GetWrappedTrans(),
1122                                            &entry_kernels);
1123 
1124   for (std::vector<const syncable::EntryKernel*>::const_iterator it =
1125            entry_kernels.begin(); it != entry_kernels.end(); ++it) {
1126     result->Append((*it)->ToValue(trans.GetCryptographer()));
1127   }
1128 
1129   return JsArgList(&return_args);
1130 }
1131 
GetChildNodeIds(const JsArgList & args)1132 JsArgList SyncManagerImpl::GetChildNodeIds(const JsArgList& args) {
1133   base::ListValue return_args;
1134   base::ListValue* child_ids = new base::ListValue();
1135   return_args.Append(child_ids);
1136   int64 id = GetId(args.Get(), 0);
1137   if (id != kInvalidId) {
1138     ReadTransaction trans(FROM_HERE, GetUserShare());
1139     syncable::Directory::Metahandles child_handles;
1140     trans.GetDirectory()->GetChildHandlesByHandle(trans.GetWrappedTrans(),
1141                                                   id, &child_handles);
1142     for (syncable::Directory::Metahandles::const_iterator it =
1143              child_handles.begin(); it != child_handles.end(); ++it) {
1144       child_ids->Append(new base::StringValue(base::Int64ToString(*it)));
1145     }
1146   }
1147   return JsArgList(&return_args);
1148 }
1149 
UpdateNotificationInfo(const ObjectIdInvalidationMap & invalidation_map)1150 void SyncManagerImpl::UpdateNotificationInfo(
1151     const ObjectIdInvalidationMap& invalidation_map) {
1152   ObjectIdSet ids = invalidation_map.GetObjectIds();
1153   for (ObjectIdSet::const_iterator it = ids.begin(); it != ids.end(); ++it) {
1154     ModelType type = UNSPECIFIED;
1155     if (!ObjectIdToRealModelType(*it, &type)) {
1156       continue;
1157     }
1158     const SingleObjectInvalidationSet& type_invalidations =
1159         invalidation_map.ForObject(*it);
1160     for (SingleObjectInvalidationSet::const_iterator inv_it =
1161          type_invalidations.begin(); inv_it != type_invalidations.end();
1162          ++inv_it) {
1163       NotificationInfo* info = &notification_info_map_[type];
1164       info->total_count++;
1165       std::string payload =
1166           inv_it->is_unknown_version() ? "UNKNOWN" : inv_it->payload();
1167       info->payload = payload;
1168     }
1169   }
1170 }
1171 
OnInvalidatorStateChange(InvalidatorState state)1172 void SyncManagerImpl::OnInvalidatorStateChange(InvalidatorState state) {
1173   DCHECK(thread_checker_.CalledOnValidThread());
1174 
1175   const std::string& state_str = InvalidatorStateToString(state);
1176   invalidator_state_ = state;
1177   DVLOG(1) << "Invalidator state changed to: " << state_str;
1178   const bool notifications_enabled =
1179       (invalidator_state_ == INVALIDATIONS_ENABLED);
1180   allstatus_.SetNotificationsEnabled(notifications_enabled);
1181   scheduler_->SetNotificationsEnabled(notifications_enabled);
1182 
1183   if (js_event_handler_.IsInitialized()) {
1184     base::DictionaryValue details;
1185     details.SetString("state", state_str);
1186     js_event_handler_.Call(FROM_HERE,
1187                            &JsEventHandler::HandleJsEvent,
1188                            "onNotificationStateChange",
1189                            JsEventDetails(&details));
1190   }
1191 }
1192 
OnIncomingInvalidation(const ObjectIdInvalidationMap & invalidation_map)1193 void SyncManagerImpl::OnIncomingInvalidation(
1194     const ObjectIdInvalidationMap& invalidation_map) {
1195   DCHECK(thread_checker_.CalledOnValidThread());
1196 
1197   // We should never receive IDs from non-sync objects.
1198   ObjectIdSet ids = invalidation_map.GetObjectIds();
1199   for (ObjectIdSet::const_iterator it = ids.begin(); it != ids.end(); ++it) {
1200     ModelType type;
1201     if (!ObjectIdToRealModelType(*it, &type)) {
1202       DLOG(WARNING) << "Notification has invalid id: " << ObjectIdToString(*it);
1203     }
1204   }
1205 
1206   if (invalidation_map.Empty()) {
1207     LOG(WARNING) << "Sync received invalidation without any type information.";
1208   } else {
1209     allstatus_.IncrementNudgeCounter(NUDGE_SOURCE_NOTIFICATION);
1210     scheduler_->ScheduleInvalidationNudge(
1211         TimeDelta::FromMilliseconds(kSyncSchedulerDelayMsec),
1212         invalidation_map, FROM_HERE);
1213     allstatus_.IncrementNotificationsReceived();
1214     UpdateNotificationInfo(invalidation_map);
1215     debug_info_event_listener_.OnIncomingNotification(invalidation_map);
1216   }
1217 
1218   if (js_event_handler_.IsInitialized()) {
1219     base::DictionaryValue details;
1220     base::ListValue* changed_types = new base::ListValue();
1221     details.Set("changedTypes", changed_types);
1222 
1223     ObjectIdSet id_set = invalidation_map.GetObjectIds();
1224     ModelTypeSet nudged_types = ObjectIdSetToModelTypeSet(id_set);
1225     DCHECK(!nudged_types.Empty());
1226     for (ModelTypeSet::Iterator it = nudged_types.First();
1227          it.Good(); it.Inc()) {
1228       const std::string model_type_str = ModelTypeToString(it.Get());
1229       changed_types->Append(new base::StringValue(model_type_str));
1230     }
1231     details.SetString("source", "REMOTE_INVALIDATION");
1232     js_event_handler_.Call(FROM_HERE,
1233                            &JsEventHandler::HandleJsEvent,
1234                            "onIncomingNotification",
1235                            JsEventDetails(&details));
1236   }
1237 }
1238 
RefreshTypes(ModelTypeSet types)1239 void SyncManagerImpl::RefreshTypes(ModelTypeSet types) {
1240   DCHECK(thread_checker_.CalledOnValidThread());
1241   if (types.Empty()) {
1242     LOG(WARNING) << "Sync received refresh request with no types specified.";
1243   } else {
1244     allstatus_.IncrementNudgeCounter(NUDGE_SOURCE_LOCAL_REFRESH);
1245     scheduler_->ScheduleLocalRefreshRequest(
1246         TimeDelta::FromMilliseconds(kSyncRefreshDelayMsec),
1247         types, FROM_HERE);
1248   }
1249 
1250   if (js_event_handler_.IsInitialized()) {
1251     base::DictionaryValue details;
1252     base::ListValue* changed_types = new base::ListValue();
1253     details.Set("changedTypes", changed_types);
1254     for (ModelTypeSet::Iterator it = types.First(); it.Good(); it.Inc()) {
1255       const std::string& model_type_str =
1256           ModelTypeToString(it.Get());
1257       changed_types->Append(new base::StringValue(model_type_str));
1258     }
1259     details.SetString("source", "LOCAL_INVALIDATION");
1260     js_event_handler_.Call(FROM_HERE,
1261                            &JsEventHandler::HandleJsEvent,
1262                            "onIncomingNotification",
1263                            JsEventDetails(&details));
1264   }
1265 }
1266 
GetDetailedStatus() const1267 SyncStatus SyncManagerImpl::GetDetailedStatus() const {
1268   return allstatus_.status();
1269 }
1270 
SaveChanges()1271 void SyncManagerImpl::SaveChanges() {
1272   directory()->SaveChanges();
1273 }
1274 
GetUserShare()1275 UserShare* SyncManagerImpl::GetUserShare() {
1276   DCHECK(initialized_);
1277   return &share_;
1278 }
1279 
cache_guid()1280 const std::string SyncManagerImpl::cache_guid() {
1281   DCHECK(initialized_);
1282   return directory()->cache_guid();
1283 }
1284 
ReceivedExperiment(Experiments * experiments)1285 bool SyncManagerImpl::ReceivedExperiment(Experiments* experiments) {
1286   ReadTransaction trans(FROM_HERE, GetUserShare());
1287   ReadNode nigori_node(&trans);
1288   if (nigori_node.InitByTagLookup(kNigoriTag) != BaseNode::INIT_OK) {
1289     DVLOG(1) << "Couldn't find Nigori node.";
1290     return false;
1291   }
1292   bool found_experiment = false;
1293 
1294   ReadNode autofill_culling_node(&trans);
1295   if (autofill_culling_node.InitByClientTagLookup(
1296           syncer::EXPERIMENTS,
1297           syncer::kAutofillCullingTag) == BaseNode::INIT_OK &&
1298       autofill_culling_node.GetExperimentsSpecifics().
1299           autofill_culling().enabled()) {
1300     experiments->autofill_culling = true;
1301     found_experiment = true;
1302   }
1303 
1304   ReadNode favicon_sync_node(&trans);
1305   if (favicon_sync_node.InitByClientTagLookup(
1306           syncer::EXPERIMENTS,
1307           syncer::kFaviconSyncTag) == BaseNode::INIT_OK) {
1308     experiments->favicon_sync_limit =
1309         favicon_sync_node.GetExperimentsSpecifics().favicon_sync().
1310             favicon_sync_limit();
1311     found_experiment = true;
1312   }
1313 
1314   ReadNode pre_commit_update_avoidance_node(&trans);
1315   if (pre_commit_update_avoidance_node.InitByClientTagLookup(
1316           syncer::EXPERIMENTS,
1317           syncer::kPreCommitUpdateAvoidanceTag) == BaseNode::INIT_OK) {
1318     session_context_->set_server_enabled_pre_commit_update_avoidance(
1319         pre_commit_update_avoidance_node.GetExperimentsSpecifics().
1320             pre_commit_update_avoidance().enabled());
1321     // We don't bother setting found_experiment.  The frontend doesn't need to
1322     // know about this.
1323   }
1324 
1325   return found_experiment;
1326 }
1327 
HasUnsyncedItems()1328 bool SyncManagerImpl::HasUnsyncedItems() {
1329   ReadTransaction trans(FROM_HERE, GetUserShare());
1330   return (trans.GetWrappedTrans()->directory()->unsynced_entity_count() != 0);
1331 }
1332 
GetEncryptionHandler()1333 SyncEncryptionHandler* SyncManagerImpl::GetEncryptionHandler() {
1334   return sync_encryption_handler_.get();
1335 }
1336 
1337 // static.
GetDefaultNudgeDelay()1338 int SyncManagerImpl::GetDefaultNudgeDelay() {
1339   return kDefaultNudgeDelayMilliseconds;
1340 }
1341 
1342 // static.
GetPreferencesNudgeDelay()1343 int SyncManagerImpl::GetPreferencesNudgeDelay() {
1344   return kPreferencesNudgeDelayMilliseconds;
1345 }
1346 
1347 }  // namespace syncer
1348