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 = ¬ification_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