OLD | NEW |
| (Empty) |
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 "components/sync/core_impl/sync_manager_impl.h" | |
6 | |
7 #include <stddef.h> | |
8 | |
9 #include <utility> | |
10 | |
11 #include "base/base64.h" | |
12 #include "base/bind.h" | |
13 #include "base/callback.h" | |
14 #include "base/compiler_specific.h" | |
15 #include "base/json/json_writer.h" | |
16 #include "base/memory/ptr_util.h" | |
17 #include "base/memory/ref_counted.h" | |
18 #include "base/metrics/histogram_macros.h" | |
19 #include "base/observer_list.h" | |
20 #include "base/strings/string_number_conversions.h" | |
21 #include "base/threading/thread_task_runner_handle.h" | |
22 #include "base/values.h" | |
23 #include "components/sync/base/cancelation_signal.h" | |
24 #include "components/sync/base/experiments.h" | |
25 #include "components/sync/base/invalidation_interface.h" | |
26 #include "components/sync/base/model_type.h" | |
27 #include "components/sync/core/configure_reason.h" | |
28 #include "components/sync/core/internal_components_factory.h" | |
29 #include "components/sync/core_impl/model_type_connector_proxy.h" | |
30 #include "components/sync/core_impl/syncapi_internal.h" | |
31 #include "components/sync/engine/net/http_post_provider_factory.h" | |
32 #include "components/sync/engine/polling_constants.h" | |
33 #include "components/sync/engine_impl/cycle/directory_type_debug_info_emitter.h" | |
34 #include "components/sync/engine_impl/net/sync_server_connection_manager.h" | |
35 #include "components/sync/engine_impl/sync_scheduler.h" | |
36 #include "components/sync/engine_impl/syncer_types.h" | |
37 #include "components/sync/protocol/proto_value_conversions.h" | |
38 #include "components/sync/protocol/sync.pb.h" | |
39 #include "components/sync/syncable/base_node.h" | |
40 #include "components/sync/syncable/directory.h" | |
41 #include "components/sync/syncable/entry.h" | |
42 #include "components/sync/syncable/in_memory_directory_backing_store.h" | |
43 #include "components/sync/syncable/on_disk_directory_backing_store.h" | |
44 #include "components/sync/syncable/read_node.h" | |
45 #include "components/sync/syncable/read_transaction.h" | |
46 #include "components/sync/syncable/write_node.h" | |
47 #include "components/sync/syncable/write_transaction.h" | |
48 | |
49 using base::TimeDelta; | |
50 using sync_pb::GetUpdatesCallerInfo; | |
51 | |
52 class GURL; | |
53 | |
54 namespace syncer { | |
55 | |
56 using syncable::ImmutableWriteTransactionInfo; | |
57 using syncable::SPECIFICS; | |
58 using syncable::UNIQUE_POSITION; | |
59 | |
60 namespace { | |
61 | |
62 GetUpdatesCallerInfo::GetUpdatesSource GetSourceFromReason( | |
63 ConfigureReason reason) { | |
64 switch (reason) { | |
65 case CONFIGURE_REASON_RECONFIGURATION: | |
66 return GetUpdatesCallerInfo::RECONFIGURATION; | |
67 case CONFIGURE_REASON_MIGRATION: | |
68 return GetUpdatesCallerInfo::MIGRATION; | |
69 case CONFIGURE_REASON_NEW_CLIENT: | |
70 return GetUpdatesCallerInfo::NEW_CLIENT; | |
71 case CONFIGURE_REASON_NEWLY_ENABLED_DATA_TYPE: | |
72 case CONFIGURE_REASON_CRYPTO: | |
73 case CONFIGURE_REASON_CATCH_UP: | |
74 return GetUpdatesCallerInfo::NEWLY_SUPPORTED_DATATYPE; | |
75 case CONFIGURE_REASON_PROGRAMMATIC: | |
76 return GetUpdatesCallerInfo::PROGRAMMATIC; | |
77 case CONFIGURE_REASON_UNKNOWN: | |
78 NOTREACHED(); | |
79 } | |
80 return GetUpdatesCallerInfo::UNKNOWN; | |
81 } | |
82 | |
83 } // namespace | |
84 | |
85 SyncManagerImpl::SyncManagerImpl(const std::string& name) | |
86 : name_(name), | |
87 change_delegate_(NULL), | |
88 initialized_(false), | |
89 observing_network_connectivity_changes_(false), | |
90 weak_ptr_factory_(this) { | |
91 // Pre-fill |notification_info_map_|. | |
92 for (int i = FIRST_REAL_MODEL_TYPE; i < MODEL_TYPE_COUNT; ++i) { | |
93 notification_info_map_.insert( | |
94 std::make_pair(ModelTypeFromInt(i), NotificationInfo())); | |
95 } | |
96 } | |
97 | |
98 SyncManagerImpl::~SyncManagerImpl() { | |
99 DCHECK(thread_checker_.CalledOnValidThread()); | |
100 CHECK(!initialized_); | |
101 } | |
102 | |
103 SyncManagerImpl::NotificationInfo::NotificationInfo() : total_count(0) {} | |
104 SyncManagerImpl::NotificationInfo::~NotificationInfo() {} | |
105 | |
106 base::DictionaryValue* SyncManagerImpl::NotificationInfo::ToValue() const { | |
107 base::DictionaryValue* value = new base::DictionaryValue(); | |
108 value->SetInteger("totalCount", total_count); | |
109 value->SetString("payload", payload); | |
110 return value; | |
111 } | |
112 | |
113 bool SyncManagerImpl::VisiblePositionsDiffer( | |
114 const syncable::EntryKernelMutation& mutation) const { | |
115 const syncable::EntryKernel& a = mutation.original; | |
116 const syncable::EntryKernel& b = mutation.mutated; | |
117 if (!b.ShouldMaintainPosition()) | |
118 return false; | |
119 if (!a.ref(UNIQUE_POSITION).Equals(b.ref(UNIQUE_POSITION))) | |
120 return true; | |
121 if (a.ref(syncable::PARENT_ID) != b.ref(syncable::PARENT_ID)) | |
122 return true; | |
123 return false; | |
124 } | |
125 | |
126 bool SyncManagerImpl::VisiblePropertiesDiffer( | |
127 const syncable::EntryKernelMutation& mutation, | |
128 Cryptographer* cryptographer) const { | |
129 const syncable::EntryKernel& a = mutation.original; | |
130 const syncable::EntryKernel& b = mutation.mutated; | |
131 const sync_pb::EntitySpecifics& a_specifics = a.ref(SPECIFICS); | |
132 const sync_pb::EntitySpecifics& b_specifics = b.ref(SPECIFICS); | |
133 DCHECK_EQ(GetModelTypeFromSpecifics(a_specifics), | |
134 GetModelTypeFromSpecifics(b_specifics)); | |
135 ModelType model_type = GetModelTypeFromSpecifics(b_specifics); | |
136 // Suppress updates to items that aren't tracked by any browser model. | |
137 if (model_type < FIRST_REAL_MODEL_TYPE || | |
138 !a.ref(syncable::UNIQUE_SERVER_TAG).empty()) { | |
139 return false; | |
140 } | |
141 if (a.ref(syncable::IS_DIR) != b.ref(syncable::IS_DIR)) | |
142 return true; | |
143 if (!AreSpecificsEqual(cryptographer, a.ref(syncable::SPECIFICS), | |
144 b.ref(syncable::SPECIFICS))) { | |
145 return true; | |
146 } | |
147 if (!AreAttachmentMetadataEqual(a.ref(syncable::ATTACHMENT_METADATA), | |
148 b.ref(syncable::ATTACHMENT_METADATA))) { | |
149 return true; | |
150 } | |
151 // We only care if the name has changed if neither specifics is encrypted | |
152 // (encrypted nodes blow away the NON_UNIQUE_NAME). | |
153 if (!a_specifics.has_encrypted() && !b_specifics.has_encrypted() && | |
154 a.ref(syncable::NON_UNIQUE_NAME) != b.ref(syncable::NON_UNIQUE_NAME)) | |
155 return true; | |
156 if (VisiblePositionsDiffer(mutation)) | |
157 return true; | |
158 return false; | |
159 } | |
160 | |
161 ModelTypeSet SyncManagerImpl::InitialSyncEndedTypes() { | |
162 DCHECK(initialized_); | |
163 return model_type_registry_->GetInitialSyncEndedTypes(); | |
164 } | |
165 | |
166 ModelTypeSet SyncManagerImpl::GetTypesWithEmptyProgressMarkerToken( | |
167 ModelTypeSet types) { | |
168 ModelTypeSet result; | |
169 for (ModelTypeSet::Iterator i = types.First(); i.Good(); i.Inc()) { | |
170 sync_pb::DataTypeProgressMarker marker; | |
171 directory()->GetDownloadProgress(i.Get(), &marker); | |
172 | |
173 if (marker.token().empty()) | |
174 result.Put(i.Get()); | |
175 } | |
176 return result; | |
177 } | |
178 | |
179 void SyncManagerImpl::ConfigureSyncer( | |
180 ConfigureReason reason, | |
181 ModelTypeSet to_download, | |
182 ModelTypeSet to_purge, | |
183 ModelTypeSet to_journal, | |
184 ModelTypeSet to_unapply, | |
185 const ModelSafeRoutingInfo& new_routing_info, | |
186 const base::Closure& ready_task, | |
187 const base::Closure& retry_task) { | |
188 DCHECK(thread_checker_.CalledOnValidThread()); | |
189 DCHECK(!ready_task.is_null()); | |
190 DCHECK(initialized_); | |
191 | |
192 DVLOG(1) << "Configuring -" | |
193 << "\n\t" | |
194 << "current types: " | |
195 << ModelTypeSetToString(GetRoutingInfoTypes(new_routing_info)) | |
196 << "\n\t" | |
197 << "types to download: " << ModelTypeSetToString(to_download) | |
198 << "\n\t" | |
199 << "types to purge: " << ModelTypeSetToString(to_purge) << "\n\t" | |
200 << "types to journal: " << ModelTypeSetToString(to_journal) << "\n\t" | |
201 << "types to unapply: " << ModelTypeSetToString(to_unapply); | |
202 if (!PurgeDisabledTypes(to_purge, to_journal, to_unapply)) { | |
203 // We failed to cleanup the types. Invoke the ready task without actually | |
204 // configuring any types. The caller should detect this as a configuration | |
205 // failure and act appropriately. | |
206 ready_task.Run(); | |
207 return; | |
208 } | |
209 | |
210 ConfigurationParams params(GetSourceFromReason(reason), to_download, | |
211 new_routing_info, ready_task, retry_task); | |
212 | |
213 scheduler_->Start(SyncScheduler::CONFIGURATION_MODE, base::Time()); | |
214 scheduler_->ScheduleConfiguration(params); | |
215 } | |
216 | |
217 void SyncManagerImpl::Init(InitArgs* args) { | |
218 CHECK(!initialized_); | |
219 DCHECK(thread_checker_.CalledOnValidThread()); | |
220 DCHECK(args->post_factory.get()); | |
221 DCHECK(!args->credentials.account_id.empty()); | |
222 DCHECK(!args->credentials.sync_token.empty()); | |
223 DCHECK(!args->credentials.scope_set.empty()); | |
224 DCHECK(args->cancelation_signal); | |
225 DVLOG(1) << "SyncManager starting Init..."; | |
226 | |
227 weak_handle_this_ = MakeWeakHandle(weak_ptr_factory_.GetWeakPtr()); | |
228 | |
229 change_delegate_ = args->change_delegate; | |
230 | |
231 AddObserver(&js_sync_manager_observer_); | |
232 SetJsEventHandler(args->event_handler); | |
233 | |
234 AddObserver(&debug_info_event_listener_); | |
235 | |
236 database_path_ = args->database_location.Append( | |
237 syncable::Directory::kSyncDatabaseFilename); | |
238 report_unrecoverable_error_function_ = | |
239 args->report_unrecoverable_error_function; | |
240 | |
241 allstatus_.SetHasKeystoreKey( | |
242 !args->restored_keystore_key_for_bootstrapping.empty()); | |
243 sync_encryption_handler_.reset(new SyncEncryptionHandlerImpl( | |
244 &share_, args->encryptor, args->restored_key_for_bootstrapping, | |
245 args->restored_keystore_key_for_bootstrapping)); | |
246 sync_encryption_handler_->AddObserver(this); | |
247 sync_encryption_handler_->AddObserver(&debug_info_event_listener_); | |
248 sync_encryption_handler_->AddObserver(&js_sync_encryption_handler_observer_); | |
249 | |
250 base::FilePath absolute_db_path = database_path_; | |
251 DCHECK(absolute_db_path.IsAbsolute()); | |
252 | |
253 std::unique_ptr<syncable::DirectoryBackingStore> backing_store = | |
254 args->internal_components_factory->BuildDirectoryBackingStore( | |
255 InternalComponentsFactory::STORAGE_ON_DISK, | |
256 args->credentials.account_id, absolute_db_path); | |
257 | |
258 DCHECK(backing_store.get()); | |
259 share_.directory.reset(new syncable::Directory( | |
260 backing_store.release(), args->unrecoverable_error_handler, | |
261 report_unrecoverable_error_function_, sync_encryption_handler_.get(), | |
262 sync_encryption_handler_->GetCryptographerUnsafe())); | |
263 share_.sync_credentials = args->credentials; | |
264 | |
265 // UserShare is accessible to a lot of code that doesn't need access to the | |
266 // sync token so clear sync_token from the UserShare. | |
267 share_.sync_credentials.sync_token = ""; | |
268 | |
269 DVLOG(1) << "Username: " << args->credentials.email; | |
270 DVLOG(1) << "AccountId: " << args->credentials.account_id; | |
271 if (!OpenDirectory(args->credentials.account_id)) { | |
272 NotifyInitializationFailure(); | |
273 LOG(ERROR) << "Sync manager initialization failed!"; | |
274 return; | |
275 } | |
276 | |
277 // Now that we have opened the Directory we can restore any previously saved | |
278 // nigori specifics. | |
279 if (args->saved_nigori_state) { | |
280 sync_encryption_handler_->RestoreNigori(*args->saved_nigori_state); | |
281 args->saved_nigori_state.reset(); | |
282 } | |
283 | |
284 connection_manager_.reset(new SyncServerConnectionManager( | |
285 args->service_url.host() + args->service_url.path(), | |
286 args->service_url.EffectiveIntPort(), | |
287 args->service_url.SchemeIsCryptographic(), args->post_factory.release(), | |
288 args->cancelation_signal)); | |
289 connection_manager_->set_client_id(directory()->cache_guid()); | |
290 connection_manager_->AddListener(this); | |
291 | |
292 std::string sync_id = directory()->cache_guid(); | |
293 | |
294 DVLOG(1) << "Setting sync client ID: " << sync_id; | |
295 allstatus_.SetSyncId(sync_id); | |
296 DVLOG(1) << "Setting invalidator client ID: " << args->invalidator_client_id; | |
297 allstatus_.SetInvalidatorClientId(args->invalidator_client_id); | |
298 | |
299 model_type_registry_.reset( | |
300 new ModelTypeRegistry(args->workers, directory(), this)); | |
301 sync_encryption_handler_->AddObserver(model_type_registry_.get()); | |
302 | |
303 // Build a SyncCycleContext and store the worker in it. | |
304 DVLOG(1) << "Sync is bringing up SyncCycleContext."; | |
305 std::vector<SyncEngineEventListener*> listeners; | |
306 listeners.push_back(&allstatus_); | |
307 listeners.push_back(this); | |
308 cycle_context_ = args->internal_components_factory->BuildContext( | |
309 connection_manager_.get(), directory(), args->extensions_activity, | |
310 listeners, &debug_info_event_listener_, model_type_registry_.get(), | |
311 args->invalidator_client_id); | |
312 scheduler_ = args->internal_components_factory->BuildScheduler( | |
313 name_, cycle_context_.get(), args->cancelation_signal); | |
314 | |
315 scheduler_->Start(SyncScheduler::CONFIGURATION_MODE, base::Time()); | |
316 | |
317 initialized_ = true; | |
318 | |
319 net::NetworkChangeNotifier::AddIPAddressObserver(this); | |
320 net::NetworkChangeNotifier::AddConnectionTypeObserver(this); | |
321 observing_network_connectivity_changes_ = true; | |
322 | |
323 UpdateCredentials(args->credentials); | |
324 | |
325 NotifyInitializationSuccess(); | |
326 } | |
327 | |
328 void SyncManagerImpl::NotifyInitializationSuccess() { | |
329 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, | |
330 OnInitializationComplete( | |
331 MakeWeakHandle(weak_ptr_factory_.GetWeakPtr()), | |
332 MakeWeakHandle(debug_info_event_listener_.GetWeakPtr()), | |
333 true, InitialSyncEndedTypes())); | |
334 } | |
335 | |
336 void SyncManagerImpl::NotifyInitializationFailure() { | |
337 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, | |
338 OnInitializationComplete( | |
339 MakeWeakHandle(weak_ptr_factory_.GetWeakPtr()), | |
340 MakeWeakHandle(debug_info_event_listener_.GetWeakPtr()), | |
341 false, ModelTypeSet())); | |
342 } | |
343 | |
344 void SyncManagerImpl::OnPassphraseRequired( | |
345 PassphraseRequiredReason reason, | |
346 const sync_pb::EncryptedData& pending_keys) { | |
347 // Does nothing. | |
348 } | |
349 | |
350 void SyncManagerImpl::OnPassphraseAccepted() { | |
351 // Does nothing. | |
352 } | |
353 | |
354 void SyncManagerImpl::OnBootstrapTokenUpdated( | |
355 const std::string& bootstrap_token, | |
356 BootstrapTokenType type) { | |
357 if (type == KEYSTORE_BOOTSTRAP_TOKEN) | |
358 allstatus_.SetHasKeystoreKey(true); | |
359 } | |
360 | |
361 void SyncManagerImpl::OnEncryptedTypesChanged(ModelTypeSet encrypted_types, | |
362 bool encrypt_everything) { | |
363 allstatus_.SetEncryptedTypes(encrypted_types); | |
364 } | |
365 | |
366 void SyncManagerImpl::OnEncryptionComplete() { | |
367 // Does nothing. | |
368 } | |
369 | |
370 void SyncManagerImpl::OnCryptographerStateChanged( | |
371 Cryptographer* cryptographer) { | |
372 allstatus_.SetCryptographerReady(cryptographer->is_ready()); | |
373 allstatus_.SetCryptoHasPendingKeys(cryptographer->has_pending_keys()); | |
374 allstatus_.SetKeystoreMigrationTime( | |
375 sync_encryption_handler_->migration_time()); | |
376 } | |
377 | |
378 void SyncManagerImpl::OnPassphraseTypeChanged( | |
379 PassphraseType type, | |
380 base::Time explicit_passphrase_time) { | |
381 allstatus_.SetPassphraseType(type); | |
382 allstatus_.SetKeystoreMigrationTime( | |
383 sync_encryption_handler_->migration_time()); | |
384 } | |
385 | |
386 void SyncManagerImpl::OnLocalSetPassphraseEncryption( | |
387 const SyncEncryptionHandler::NigoriState& nigori_state) {} | |
388 | |
389 void SyncManagerImpl::StartSyncingNormally( | |
390 const ModelSafeRoutingInfo& routing_info, | |
391 base::Time last_poll_time) { | |
392 // Start the sync scheduler. | |
393 // TODO(sync): We always want the newest set of routes when we switch back | |
394 // to normal mode. Figure out how to enforce set_routing_info is always | |
395 // appropriately set and that it's only modified when switching to normal | |
396 // mode. | |
397 DCHECK(thread_checker_.CalledOnValidThread()); | |
398 cycle_context_->SetRoutingInfo(routing_info); | |
399 scheduler_->Start(SyncScheduler::NORMAL_MODE, last_poll_time); | |
400 } | |
401 | |
402 syncable::Directory* SyncManagerImpl::directory() { | |
403 return share_.directory.get(); | |
404 } | |
405 | |
406 const SyncScheduler* SyncManagerImpl::scheduler() const { | |
407 return scheduler_.get(); | |
408 } | |
409 | |
410 bool SyncManagerImpl::GetHasInvalidAuthTokenForTest() const { | |
411 return connection_manager_->HasInvalidAuthToken(); | |
412 } | |
413 | |
414 bool SyncManagerImpl::OpenDirectory(const std::string& username) { | |
415 DCHECK(!initialized_) << "Should only happen once"; | |
416 | |
417 // Set before Open(). | |
418 change_observer_ = MakeWeakHandle(js_mutation_event_observer_.AsWeakPtr()); | |
419 WeakHandle<syncable::TransactionObserver> transaction_observer( | |
420 MakeWeakHandle(js_mutation_event_observer_.AsWeakPtr())); | |
421 | |
422 syncable::DirOpenResult open_result = syncable::NOT_INITIALIZED; | |
423 open_result = directory()->Open(username, this, transaction_observer); | |
424 if (open_result != syncable::OPENED) { | |
425 LOG(ERROR) << "Could not open share for:" << username; | |
426 return false; | |
427 } | |
428 | |
429 // Unapplied datatypes (those that do not have initial sync ended set) get | |
430 // re-downloaded during any configuration. But, it's possible for a datatype | |
431 // to have a progress marker but not have initial sync ended yet, making | |
432 // it a candidate for migration. This is a problem, as the DataTypeManager | |
433 // does not support a migration while it's already in the middle of a | |
434 // configuration. As a result, any partially synced datatype can stall the | |
435 // DTM, waiting for the configuration to complete, which it never will due | |
436 // to the migration error. In addition, a partially synced nigori will | |
437 // trigger the migration logic before the backend is initialized, resulting | |
438 // in crashes. We therefore detect and purge any partially synced types as | |
439 // part of initialization. | |
440 if (!PurgePartiallySyncedTypes()) | |
441 return false; | |
442 | |
443 return true; | |
444 } | |
445 | |
446 bool SyncManagerImpl::PurgePartiallySyncedTypes() { | |
447 ModelTypeSet partially_synced_types = ModelTypeSet::All(); | |
448 partially_synced_types.RemoveAll(directory()->InitialSyncEndedTypes()); | |
449 partially_synced_types.RemoveAll( | |
450 GetTypesWithEmptyProgressMarkerToken(ModelTypeSet::All())); | |
451 | |
452 DVLOG(1) << "Purging partially synced types " | |
453 << ModelTypeSetToString(partially_synced_types); | |
454 UMA_HISTOGRAM_COUNTS("Sync.PartiallySyncedTypes", | |
455 partially_synced_types.Size()); | |
456 if (partially_synced_types.Empty()) | |
457 return true; | |
458 return directory()->PurgeEntriesWithTypeIn(partially_synced_types, | |
459 ModelTypeSet(), ModelTypeSet()); | |
460 } | |
461 | |
462 bool SyncManagerImpl::PurgeDisabledTypes(ModelTypeSet to_purge, | |
463 ModelTypeSet to_journal, | |
464 ModelTypeSet to_unapply) { | |
465 if (to_purge.Empty()) | |
466 return true; | |
467 DVLOG(1) << "Purging disabled types " << ModelTypeSetToString(to_purge); | |
468 DCHECK(to_purge.HasAll(to_journal)); | |
469 DCHECK(to_purge.HasAll(to_unapply)); | |
470 return directory()->PurgeEntriesWithTypeIn(to_purge, to_journal, to_unapply); | |
471 } | |
472 | |
473 void SyncManagerImpl::UpdateCredentials(const SyncCredentials& credentials) { | |
474 DCHECK(thread_checker_.CalledOnValidThread()); | |
475 DCHECK(initialized_); | |
476 DCHECK(!credentials.account_id.empty()); | |
477 DCHECK(!credentials.sync_token.empty()); | |
478 DCHECK(!credentials.scope_set.empty()); | |
479 cycle_context_->set_account_name(credentials.email); | |
480 | |
481 observing_network_connectivity_changes_ = true; | |
482 if (!connection_manager_->SetAuthToken(credentials.sync_token)) | |
483 return; // Auth token is known to be invalid, so exit early. | |
484 | |
485 scheduler_->OnCredentialsUpdated(); | |
486 | |
487 // TODO(zea): pass the credential age to the debug info event listener. | |
488 } | |
489 | |
490 void SyncManagerImpl::AddObserver(SyncManager::Observer* observer) { | |
491 DCHECK(thread_checker_.CalledOnValidThread()); | |
492 observers_.AddObserver(observer); | |
493 } | |
494 | |
495 void SyncManagerImpl::RemoveObserver(SyncManager::Observer* observer) { | |
496 DCHECK(thread_checker_.CalledOnValidThread()); | |
497 observers_.RemoveObserver(observer); | |
498 } | |
499 | |
500 void SyncManagerImpl::ShutdownOnSyncThread(ShutdownReason reason) { | |
501 DCHECK(thread_checker_.CalledOnValidThread()); | |
502 | |
503 // Prevent any in-flight method calls from running. Also | |
504 // invalidates |weak_handle_this_| and |change_observer_|. | |
505 weak_ptr_factory_.InvalidateWeakPtrs(); | |
506 js_mutation_event_observer_.InvalidateWeakPtrs(); | |
507 | |
508 scheduler_.reset(); | |
509 cycle_context_.reset(); | |
510 | |
511 if (model_type_registry_) | |
512 sync_encryption_handler_->RemoveObserver(model_type_registry_.get()); | |
513 | |
514 model_type_registry_.reset(); | |
515 | |
516 if (sync_encryption_handler_) { | |
517 sync_encryption_handler_->RemoveObserver(&debug_info_event_listener_); | |
518 sync_encryption_handler_->RemoveObserver(this); | |
519 } | |
520 | |
521 SetJsEventHandler(WeakHandle<JsEventHandler>()); | |
522 RemoveObserver(&js_sync_manager_observer_); | |
523 | |
524 RemoveObserver(&debug_info_event_listener_); | |
525 | |
526 // |connection_manager_| may end up being NULL here in tests (in synchronous | |
527 // initialization mode). | |
528 // | |
529 // TODO(akalin): Fix this behavior. | |
530 if (connection_manager_) | |
531 connection_manager_->RemoveListener(this); | |
532 connection_manager_.reset(); | |
533 | |
534 net::NetworkChangeNotifier::RemoveIPAddressObserver(this); | |
535 net::NetworkChangeNotifier::RemoveConnectionTypeObserver(this); | |
536 observing_network_connectivity_changes_ = false; | |
537 | |
538 if (initialized_ && directory()) { | |
539 directory()->SaveChanges(); | |
540 } | |
541 | |
542 share_.directory.reset(); | |
543 | |
544 change_delegate_ = NULL; | |
545 | |
546 initialized_ = false; | |
547 | |
548 // We reset these here, since only now we know they will not be | |
549 // accessed from other threads (since we shut down everything). | |
550 change_observer_.Reset(); | |
551 weak_handle_this_.Reset(); | |
552 } | |
553 | |
554 void SyncManagerImpl::OnIPAddressChanged() { | |
555 if (!observing_network_connectivity_changes_) { | |
556 DVLOG(1) << "IP address change dropped."; | |
557 return; | |
558 } | |
559 DVLOG(1) << "IP address change detected."; | |
560 OnNetworkConnectivityChangedImpl(); | |
561 } | |
562 | |
563 void SyncManagerImpl::OnConnectionTypeChanged( | |
564 net::NetworkChangeNotifier::ConnectionType) { | |
565 if (!observing_network_connectivity_changes_) { | |
566 DVLOG(1) << "Connection type change dropped."; | |
567 return; | |
568 } | |
569 DVLOG(1) << "Connection type change detected."; | |
570 OnNetworkConnectivityChangedImpl(); | |
571 } | |
572 | |
573 void SyncManagerImpl::OnNetworkConnectivityChangedImpl() { | |
574 DCHECK(thread_checker_.CalledOnValidThread()); | |
575 scheduler_->OnConnectionStatusChange(); | |
576 } | |
577 | |
578 void SyncManagerImpl::OnServerConnectionEvent( | |
579 const ServerConnectionEvent& event) { | |
580 DCHECK(thread_checker_.CalledOnValidThread()); | |
581 if (event.connection_code == HttpResponse::SERVER_CONNECTION_OK) { | |
582 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, | |
583 OnConnectionStatusChange(CONNECTION_OK)); | |
584 } | |
585 | |
586 if (event.connection_code == HttpResponse::SYNC_AUTH_ERROR) { | |
587 observing_network_connectivity_changes_ = false; | |
588 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, | |
589 OnConnectionStatusChange(CONNECTION_AUTH_ERROR)); | |
590 } | |
591 | |
592 if (event.connection_code == HttpResponse::SYNC_SERVER_ERROR) { | |
593 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, | |
594 OnConnectionStatusChange(CONNECTION_SERVER_ERROR)); | |
595 } | |
596 } | |
597 | |
598 void SyncManagerImpl::HandleTransactionCompleteChangeEvent( | |
599 ModelTypeSet models_with_changes) { | |
600 // This notification happens immediately after the transaction mutex is | |
601 // released. This allows work to be performed without blocking other threads | |
602 // from acquiring a transaction. | |
603 if (!change_delegate_) | |
604 return; | |
605 | |
606 // Call commit. | |
607 for (ModelTypeSet::Iterator it = models_with_changes.First(); it.Good(); | |
608 it.Inc()) { | |
609 change_delegate_->OnChangesComplete(it.Get()); | |
610 change_observer_.Call( | |
611 FROM_HERE, &SyncManager::ChangeObserver::OnChangesComplete, it.Get()); | |
612 } | |
613 } | |
614 | |
615 ModelTypeSet SyncManagerImpl::HandleTransactionEndingChangeEvent( | |
616 const ImmutableWriteTransactionInfo& write_transaction_info, | |
617 syncable::BaseTransaction* trans) { | |
618 // This notification happens immediately before a syncable WriteTransaction | |
619 // falls out of scope. It happens while the channel mutex is still held, | |
620 // and while the transaction mutex is held, so it cannot be re-entrant. | |
621 if (!change_delegate_ || change_records_.empty()) | |
622 return ModelTypeSet(); | |
623 | |
624 // This will continue the WriteTransaction using a read only wrapper. | |
625 // This is the last chance for read to occur in the WriteTransaction | |
626 // that's closing. This special ReadTransaction will not close the | |
627 // underlying transaction. | |
628 ReadTransaction read_trans(GetUserShare(), trans); | |
629 | |
630 ModelTypeSet models_with_changes; | |
631 for (ChangeRecordMap::const_iterator it = change_records_.begin(); | |
632 it != change_records_.end(); ++it) { | |
633 DCHECK(!it->second.Get().empty()); | |
634 ModelType type = ModelTypeFromInt(it->first); | |
635 change_delegate_->OnChangesApplied( | |
636 type, trans->directory()->GetTransactionVersion(type), &read_trans, | |
637 it->second); | |
638 change_observer_.Call(FROM_HERE, | |
639 &SyncManager::ChangeObserver::OnChangesApplied, type, | |
640 write_transaction_info.Get().id, it->second); | |
641 models_with_changes.Put(type); | |
642 } | |
643 change_records_.clear(); | |
644 return models_with_changes; | |
645 } | |
646 | |
647 void SyncManagerImpl::HandleCalculateChangesChangeEventFromSyncApi( | |
648 const ImmutableWriteTransactionInfo& write_transaction_info, | |
649 syncable::BaseTransaction* trans, | |
650 std::vector<int64_t>* entries_changed) { | |
651 // We have been notified about a user action changing a sync model. | |
652 LOG_IF(WARNING, !change_records_.empty()) | |
653 << "CALCULATE_CHANGES called with unapplied old changes."; | |
654 | |
655 // The mutated model type, or UNSPECIFIED if nothing was mutated. | |
656 ModelTypeSet mutated_model_types; | |
657 | |
658 const syncable::ImmutableEntryKernelMutationMap& mutations = | |
659 write_transaction_info.Get().mutations; | |
660 for (syncable::EntryKernelMutationMap::const_iterator it = | |
661 mutations.Get().begin(); | |
662 it != mutations.Get().end(); ++it) { | |
663 if (!it->second.mutated.ref(syncable::IS_UNSYNCED)) { | |
664 continue; | |
665 } | |
666 | |
667 ModelType model_type = | |
668 GetModelTypeFromSpecifics(it->second.mutated.ref(SPECIFICS)); | |
669 if (model_type < FIRST_REAL_MODEL_TYPE) { | |
670 NOTREACHED() << "Permanent or underspecified item changed via syncapi."; | |
671 continue; | |
672 } | |
673 | |
674 // Found real mutation. | |
675 if (model_type != UNSPECIFIED) { | |
676 mutated_model_types.Put(model_type); | |
677 entries_changed->push_back(it->second.mutated.ref(syncable::META_HANDLE)); | |
678 } | |
679 } | |
680 | |
681 // Nudge if necessary. | |
682 if (!mutated_model_types.Empty()) { | |
683 if (weak_handle_this_.IsInitialized()) { | |
684 weak_handle_this_.Call(FROM_HERE, | |
685 &SyncManagerImpl::RequestNudgeForDataTypes, | |
686 FROM_HERE, mutated_model_types); | |
687 } else { | |
688 NOTREACHED(); | |
689 } | |
690 } | |
691 } | |
692 | |
693 void SyncManagerImpl::SetExtraChangeRecordData( | |
694 int64_t id, | |
695 ModelType type, | |
696 ChangeReorderBuffer* buffer, | |
697 Cryptographer* cryptographer, | |
698 const syncable::EntryKernel& original, | |
699 bool existed_before, | |
700 bool exists_now) { | |
701 // If this is a deletion and the datatype was encrypted, we need to decrypt it | |
702 // and attach it to the buffer. | |
703 if (!exists_now && existed_before) { | |
704 sync_pb::EntitySpecifics original_specifics(original.ref(SPECIFICS)); | |
705 if (type == PASSWORDS) { | |
706 // Passwords must use their own legacy ExtraPasswordChangeRecordData. | |
707 std::unique_ptr<sync_pb::PasswordSpecificsData> data( | |
708 DecryptPasswordSpecifics(original_specifics, cryptographer)); | |
709 if (!data) { | |
710 NOTREACHED(); | |
711 return; | |
712 } | |
713 buffer->SetExtraDataForId(id, new ExtraPasswordChangeRecordData(*data)); | |
714 } else if (original_specifics.has_encrypted()) { | |
715 // All other datatypes can just create a new unencrypted specifics and | |
716 // attach it. | |
717 const sync_pb::EncryptedData& encrypted = original_specifics.encrypted(); | |
718 if (!cryptographer->Decrypt(encrypted, &original_specifics)) { | |
719 NOTREACHED(); | |
720 return; | |
721 } | |
722 } | |
723 buffer->SetSpecificsForId(id, original_specifics); | |
724 } | |
725 } | |
726 | |
727 void SyncManagerImpl::HandleCalculateChangesChangeEventFromSyncer( | |
728 const ImmutableWriteTransactionInfo& write_transaction_info, | |
729 syncable::BaseTransaction* trans, | |
730 std::vector<int64_t>* entries_changed) { | |
731 // We only expect one notification per sync step, so change_buffers_ should | |
732 // contain no pending entries. | |
733 LOG_IF(WARNING, !change_records_.empty()) | |
734 << "CALCULATE_CHANGES called with unapplied old changes."; | |
735 | |
736 ChangeReorderBuffer change_buffers[MODEL_TYPE_COUNT]; | |
737 | |
738 Cryptographer* crypto = directory()->GetCryptographer(trans); | |
739 const syncable::ImmutableEntryKernelMutationMap& mutations = | |
740 write_transaction_info.Get().mutations; | |
741 for (syncable::EntryKernelMutationMap::const_iterator it = | |
742 mutations.Get().begin(); | |
743 it != mutations.Get().end(); ++it) { | |
744 bool existed_before = !it->second.original.ref(syncable::IS_DEL); | |
745 bool exists_now = !it->second.mutated.ref(syncable::IS_DEL); | |
746 | |
747 // Omit items that aren't associated with a model. | |
748 ModelType type = | |
749 GetModelTypeFromSpecifics(it->second.mutated.ref(SPECIFICS)); | |
750 if (type < FIRST_REAL_MODEL_TYPE) | |
751 continue; | |
752 | |
753 int64_t handle = it->first; | |
754 if (exists_now && !existed_before) | |
755 change_buffers[type].PushAddedItem(handle); | |
756 else if (!exists_now && existed_before) | |
757 change_buffers[type].PushDeletedItem(handle); | |
758 else if (exists_now && existed_before && | |
759 VisiblePropertiesDiffer(it->second, crypto)) | |
760 change_buffers[type].PushUpdatedItem(handle); | |
761 | |
762 SetExtraChangeRecordData(handle, type, &change_buffers[type], crypto, | |
763 it->second.original, existed_before, exists_now); | |
764 } | |
765 | |
766 ReadTransaction read_trans(GetUserShare(), trans); | |
767 for (int i = FIRST_REAL_MODEL_TYPE; i < MODEL_TYPE_COUNT; ++i) { | |
768 if (!change_buffers[i].IsEmpty()) { | |
769 if (change_buffers[i].GetAllChangesInTreeOrder(&read_trans, | |
770 &(change_records_[i]))) { | |
771 for (size_t j = 0; j < change_records_[i].Get().size(); ++j) | |
772 entries_changed->push_back((change_records_[i].Get())[j].id); | |
773 } | |
774 if (change_records_[i].Get().empty()) | |
775 change_records_.erase(i); | |
776 } | |
777 } | |
778 } | |
779 | |
780 void SyncManagerImpl::RequestNudgeForDataTypes( | |
781 const tracked_objects::Location& nudge_location, | |
782 ModelTypeSet types) { | |
783 debug_info_event_listener_.OnNudgeFromDatatype(types.First().Get()); | |
784 | |
785 scheduler_->ScheduleLocalNudge(types, nudge_location); | |
786 } | |
787 | |
788 void SyncManagerImpl::NudgeForInitialDownload(ModelType type) { | |
789 DCHECK(thread_checker_.CalledOnValidThread()); | |
790 scheduler_->ScheduleInitialSyncNudge(type); | |
791 } | |
792 | |
793 void SyncManagerImpl::NudgeForCommit(ModelType type) { | |
794 DCHECK(thread_checker_.CalledOnValidThread()); | |
795 RequestNudgeForDataTypes(FROM_HERE, ModelTypeSet(type)); | |
796 } | |
797 | |
798 void SyncManagerImpl::NudgeForRefresh(ModelType type) { | |
799 DCHECK(thread_checker_.CalledOnValidThread()); | |
800 RefreshTypes(ModelTypeSet(type)); | |
801 } | |
802 | |
803 void SyncManagerImpl::OnSyncCycleEvent(const SyncCycleEvent& event) { | |
804 DCHECK(thread_checker_.CalledOnValidThread()); | |
805 // Only send an event if this is due to a cycle ending and this cycle | |
806 // concludes a canonical "sync" process; that is, based on what is known | |
807 // locally we are "all happy" and up to date. There may be new changes on | |
808 // the server, but we'll get them on a subsequent sync. | |
809 // | |
810 // Notifications are sent at the end of every sync cycle, regardless of | |
811 // whether we should sync again. | |
812 if (event.what_happened == SyncCycleEvent::SYNC_CYCLE_ENDED) { | |
813 if (!initialized_) { | |
814 DVLOG(1) << "OnSyncCycleCompleted not sent because sync api is not " | |
815 << "initialized"; | |
816 return; | |
817 } | |
818 | |
819 DVLOG(1) << "Sending OnSyncCycleCompleted"; | |
820 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, | |
821 OnSyncCycleCompleted(event.snapshot)); | |
822 } | |
823 } | |
824 | |
825 void SyncManagerImpl::OnActionableError(const SyncProtocolError& error) { | |
826 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, | |
827 OnActionableError(error)); | |
828 } | |
829 | |
830 void SyncManagerImpl::OnRetryTimeChanged(base::Time) {} | |
831 | |
832 void SyncManagerImpl::OnThrottledTypesChanged(ModelTypeSet) {} | |
833 | |
834 void SyncManagerImpl::OnMigrationRequested(ModelTypeSet types) { | |
835 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, | |
836 OnMigrationRequested(types)); | |
837 } | |
838 | |
839 void SyncManagerImpl::OnProtocolEvent(const ProtocolEvent& event) { | |
840 protocol_event_buffer_.RecordProtocolEvent(event); | |
841 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, OnProtocolEvent(event)); | |
842 } | |
843 | |
844 void SyncManagerImpl::SetJsEventHandler( | |
845 const WeakHandle<JsEventHandler>& event_handler) { | |
846 js_sync_manager_observer_.SetJsEventHandler(event_handler); | |
847 js_mutation_event_observer_.SetJsEventHandler(event_handler); | |
848 js_sync_encryption_handler_observer_.SetJsEventHandler(event_handler); | |
849 } | |
850 | |
851 void SyncManagerImpl::SetInvalidatorEnabled(bool invalidator_enabled) { | |
852 DCHECK(thread_checker_.CalledOnValidThread()); | |
853 | |
854 DVLOG(1) << "Invalidator enabled state is now: " << invalidator_enabled; | |
855 allstatus_.SetNotificationsEnabled(invalidator_enabled); | |
856 scheduler_->SetNotificationsEnabled(invalidator_enabled); | |
857 } | |
858 | |
859 void SyncManagerImpl::OnIncomingInvalidation( | |
860 ModelType type, | |
861 std::unique_ptr<InvalidationInterface> invalidation) { | |
862 DCHECK(thread_checker_.CalledOnValidThread()); | |
863 | |
864 allstatus_.IncrementNotificationsReceived(); | |
865 scheduler_->ScheduleInvalidationNudge(type, std::move(invalidation), | |
866 FROM_HERE); | |
867 } | |
868 | |
869 void SyncManagerImpl::RefreshTypes(ModelTypeSet types) { | |
870 DCHECK(thread_checker_.CalledOnValidThread()); | |
871 if (types.Empty()) { | |
872 LOG(WARNING) << "Sync received refresh request with no types specified."; | |
873 } else { | |
874 scheduler_->ScheduleLocalRefreshRequest(types, FROM_HERE); | |
875 } | |
876 } | |
877 | |
878 SyncStatus SyncManagerImpl::GetDetailedStatus() const { | |
879 return allstatus_.status(); | |
880 } | |
881 | |
882 void SyncManagerImpl::SaveChanges() { | |
883 directory()->SaveChanges(); | |
884 } | |
885 | |
886 UserShare* SyncManagerImpl::GetUserShare() { | |
887 DCHECK(initialized_); | |
888 return &share_; | |
889 } | |
890 | |
891 std::unique_ptr<ModelTypeConnector> | |
892 SyncManagerImpl::GetModelTypeConnectorProxy() { | |
893 DCHECK(initialized_); | |
894 return base::MakeUnique<ModelTypeConnectorProxy>( | |
895 base::ThreadTaskRunnerHandle::Get(), model_type_registry_->AsWeakPtr()); | |
896 } | |
897 | |
898 const std::string SyncManagerImpl::cache_guid() { | |
899 DCHECK(initialized_); | |
900 return directory()->cache_guid(); | |
901 } | |
902 | |
903 bool SyncManagerImpl::ReceivedExperiment(Experiments* experiments) { | |
904 ReadTransaction trans(FROM_HERE, GetUserShare()); | |
905 ReadNode nigori_node(&trans); | |
906 if (nigori_node.InitTypeRoot(NIGORI) != BaseNode::INIT_OK) { | |
907 DVLOG(1) << "Couldn't find Nigori node."; | |
908 return false; | |
909 } | |
910 bool found_experiment = false; | |
911 | |
912 ReadNode favicon_sync_node(&trans); | |
913 if (favicon_sync_node.InitByClientTagLookup(EXPERIMENTS, kFaviconSyncTag) == | |
914 BaseNode::INIT_OK) { | |
915 experiments->favicon_sync_limit = | |
916 favicon_sync_node.GetExperimentsSpecifics() | |
917 .favicon_sync() | |
918 .favicon_sync_limit(); | |
919 found_experiment = true; | |
920 } | |
921 | |
922 ReadNode pre_commit_update_avoidance_node(&trans); | |
923 if (pre_commit_update_avoidance_node.InitByClientTagLookup( | |
924 EXPERIMENTS, kPreCommitUpdateAvoidanceTag) == BaseNode::INIT_OK) { | |
925 cycle_context_->set_server_enabled_pre_commit_update_avoidance( | |
926 pre_commit_update_avoidance_node.GetExperimentsSpecifics() | |
927 .pre_commit_update_avoidance() | |
928 .enabled()); | |
929 // We don't bother setting found_experiment. The frontend doesn't need to | |
930 // know about this. | |
931 } | |
932 | |
933 ReadNode gcm_invalidations_node(&trans); | |
934 if (gcm_invalidations_node.InitByClientTagLookup( | |
935 EXPERIMENTS, kGCMInvalidationsTag) == BaseNode::INIT_OK) { | |
936 const sync_pb::GcmInvalidationsFlags& gcm_invalidations = | |
937 gcm_invalidations_node.GetExperimentsSpecifics().gcm_invalidations(); | |
938 if (gcm_invalidations.has_enabled()) { | |
939 experiments->gcm_invalidations_enabled = gcm_invalidations.enabled(); | |
940 found_experiment = true; | |
941 } | |
942 } | |
943 | |
944 return found_experiment; | |
945 } | |
946 | |
947 bool SyncManagerImpl::HasUnsyncedItems() { | |
948 ReadTransaction trans(FROM_HERE, GetUserShare()); | |
949 return (trans.GetWrappedTrans()->directory()->unsynced_entity_count() != 0); | |
950 } | |
951 | |
952 SyncEncryptionHandler* SyncManagerImpl::GetEncryptionHandler() { | |
953 return sync_encryption_handler_.get(); | |
954 } | |
955 | |
956 std::vector<std::unique_ptr<ProtocolEvent>> | |
957 SyncManagerImpl::GetBufferedProtocolEvents() { | |
958 return protocol_event_buffer_.GetBufferedProtocolEvents(); | |
959 } | |
960 | |
961 void SyncManagerImpl::RegisterDirectoryTypeDebugInfoObserver( | |
962 TypeDebugInfoObserver* observer) { | |
963 model_type_registry_->RegisterDirectoryTypeDebugInfoObserver(observer); | |
964 } | |
965 | |
966 void SyncManagerImpl::UnregisterDirectoryTypeDebugInfoObserver( | |
967 TypeDebugInfoObserver* observer) { | |
968 model_type_registry_->UnregisterDirectoryTypeDebugInfoObserver(observer); | |
969 } | |
970 | |
971 bool SyncManagerImpl::HasDirectoryTypeDebugInfoObserver( | |
972 TypeDebugInfoObserver* observer) { | |
973 return model_type_registry_->HasDirectoryTypeDebugInfoObserver(observer); | |
974 } | |
975 | |
976 void SyncManagerImpl::RequestEmitDebugInfo() { | |
977 model_type_registry_->RequestEmitDebugInfo(); | |
978 } | |
979 | |
980 void SyncManagerImpl::ClearServerData(const ClearServerDataCallback& callback) { | |
981 DCHECK(thread_checker_.CalledOnValidThread()); | |
982 scheduler_->Start(SyncScheduler::CLEAR_SERVER_DATA_MODE, base::Time()); | |
983 ClearParams params(callback); | |
984 scheduler_->ScheduleClearServerData(params); | |
985 } | |
986 | |
987 void SyncManagerImpl::OnCookieJarChanged(bool account_mismatch, | |
988 bool empty_jar) { | |
989 DCHECK(thread_checker_.CalledOnValidThread()); | |
990 cycle_context_->set_cookie_jar_mismatch(account_mismatch); | |
991 cycle_context_->set_cookie_jar_empty(empty_jar); | |
992 } | |
993 | |
994 } // namespace syncer | |
OLD | NEW |