1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/sync/glue/data_type_manager_impl.h"
10 #include "base/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/callback.h"
13 #include "base/compiler_specific.h"
14 #include "base/debug/trace_event.h"
15 #include "base/logging.h"
16 #include "base/message_loop/message_loop.h"
17 #include "base/metrics/histogram.h"
18 #include "base/strings/stringprintf.h"
19 #include "chrome/browser/sync/glue/chrome_report_unrecoverable_error.h"
20 #include "chrome/browser/sync/glue/data_type_controller.h"
21 #include "chrome/browser/sync/glue/data_type_encryption_handler.h"
22 #include "chrome/browser/sync/glue/data_type_manager_observer.h"
23 #include "chrome/browser/sync/glue/failed_data_types_handler.h"
24 #include "content/public/browser/browser_thread.h"
25 #include "sync/internal_api/public/data_type_debug_info_listener.h"
27 using content::BrowserThread;
29 namespace browser_sync {
33 FailedDataTypesHandler::TypeErrorMap
34 GenerateCryptoErrorsForTypes(syncer::ModelTypeSet encrypted_types) {
35 FailedDataTypesHandler::TypeErrorMap crypto_errors;
36 for (syncer::ModelTypeSet::Iterator iter = encrypted_types.First();
37 iter.Good(); iter.Inc()) {
38 crypto_errors[iter.Get()] = syncer::SyncError(
40 syncer::SyncError::CRYPTO_ERROR,
49 DataTypeManagerImpl::AssociationTypesInfo::AssociationTypesInfo() {}
50 DataTypeManagerImpl::AssociationTypesInfo::~AssociationTypesInfo() {}
52 DataTypeManagerImpl::DataTypeManagerImpl(
53 const syncer::WeakHandle<syncer::DataTypeDebugInfoListener>&
55 const DataTypeController::TypeMap* controllers,
56 const browser_sync::DataTypeEncryptionHandler* encryption_handler,
57 BackendDataTypeConfigurer* configurer,
58 DataTypeManagerObserver* observer,
59 browser_sync::FailedDataTypesHandler* failed_data_types_handler)
60 : configurer_(configurer),
61 controllers_(controllers),
62 state_(DataTypeManager::STOPPED),
63 needs_reconfigure_(false),
64 last_configure_reason_(syncer::CONFIGURE_REASON_UNKNOWN),
65 debug_info_listener_(debug_info_listener),
66 model_association_manager_(controllers, this),
68 failed_data_types_handler_(failed_data_types_handler),
69 encryption_handler_(encryption_handler),
70 weak_ptr_factory_(this) {
71 DCHECK(failed_data_types_handler_);
76 DataTypeManagerImpl::~DataTypeManagerImpl() {}
78 void DataTypeManagerImpl::Configure(syncer::ModelTypeSet desired_types,
79 syncer::ConfigureReason reason) {
80 desired_types.PutAll(syncer::CoreTypes());
81 ConfigureImpl(desired_types, reason);
84 void DataTypeManagerImpl::PurgeForMigration(
85 syncer::ModelTypeSet undesired_types,
86 syncer::ConfigureReason reason) {
87 syncer::ModelTypeSet remainder = Difference(last_requested_types_,
89 ConfigureImpl(remainder, reason);
92 void DataTypeManagerImpl::ConfigureImpl(
93 syncer::ModelTypeSet desired_types,
94 syncer::ConfigureReason reason) {
95 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
96 DCHECK_NE(reason, syncer::CONFIGURE_REASON_UNKNOWN);
97 DVLOG(1) << "Configuring for " << syncer::ModelTypeSetToString(desired_types)
98 << " with reason " << reason;
99 if (state_ == STOPPING) {
100 // You can not set a configuration while stopping.
101 LOG(ERROR) << "Configuration set while stopping.";
105 if (state_ == CONFIGURED &&
106 last_requested_types_.Equals(desired_types) &&
107 reason == syncer::CONFIGURE_REASON_RECONFIGURATION &&
108 syncer::Intersection(failed_data_types_handler_->GetFailedTypes(),
109 last_requested_types_).Empty()) {
110 // If the set of enabled types hasn't changed and there are no failing
111 // types, we can exit out early.
112 DVLOG(1) << "Reconfigure with same types, bypassing confguration.";
114 ConfigureResult result(OK, last_requested_types_);
119 last_requested_types_ = desired_types;
120 last_configure_reason_ = reason;
121 // Only proceed if we're in a steady state or retrying.
122 if (state_ != STOPPED && state_ != CONFIGURED && state_ != RETRYING) {
123 DVLOG(1) << "Received configure request while configuration in flight. "
124 << "Postponing until current configuration complete.";
125 needs_reconfigure_ = true;
132 BackendDataTypeConfigurer::DataTypeConfigStateMap
133 DataTypeManagerImpl::BuildDataTypeConfigStateMap(
134 const syncer::ModelTypeSet& types_being_configured) const {
135 // 1. Get the failed types (both due to fatal and crypto errors).
136 // 2. Add the difference between last_requested_types_ and the failed types
137 // as CONFIGURE_INACTIVE.
138 // 3. Flip |types_being_configured| to CONFIGURE_ACTIVE.
139 // 4. Set non-enabled user types as DISABLED.
140 // 5. Set the fatal and crypto types to their respective states.
141 syncer::ModelTypeSet error_types =
142 failed_data_types_handler_->GetFailedTypes();
143 syncer::ModelTypeSet fatal_types =
144 failed_data_types_handler_->GetFatalErrorTypes();
145 syncer::ModelTypeSet crypto_types =
146 failed_data_types_handler_->GetCryptoErrorTypes();
148 // Types with persistence errors are only purged/resynced when they're
149 // actively being configured.
150 syncer::ModelTypeSet persistence_types =
151 failed_data_types_handler_->GetPersistenceErrorTypes();
152 persistence_types.RetainAll(types_being_configured);
154 syncer::ModelTypeSet enabled_types = last_requested_types_;
155 enabled_types.RemoveAll(error_types);
156 syncer::ModelTypeSet disabled_types =
158 syncer::Union(syncer::UserTypes(), syncer::ControlTypes()),
160 syncer::ModelTypeSet to_configure = syncer::Intersection(
161 enabled_types, types_being_configured);
162 DVLOG(1) << "Enabling: " << syncer::ModelTypeSetToString(enabled_types);
163 DVLOG(1) << "Configuring: " << syncer::ModelTypeSetToString(to_configure);
164 DVLOG(1) << "Disabling: " << syncer::ModelTypeSetToString(disabled_types);
166 BackendDataTypeConfigurer::DataTypeConfigStateMap config_state_map;
167 BackendDataTypeConfigurer::SetDataTypesState(
168 BackendDataTypeConfigurer::CONFIGURE_INACTIVE, enabled_types,
170 BackendDataTypeConfigurer::SetDataTypesState(
171 BackendDataTypeConfigurer::CONFIGURE_ACTIVE, to_configure,
173 BackendDataTypeConfigurer::SetDataTypesState(
174 BackendDataTypeConfigurer::CONFIGURE_CLEAN, persistence_types,
176 BackendDataTypeConfigurer::SetDataTypesState(
177 BackendDataTypeConfigurer::DISABLED, disabled_types,
179 BackendDataTypeConfigurer::SetDataTypesState(
180 BackendDataTypeConfigurer::FATAL, fatal_types,
182 BackendDataTypeConfigurer::SetDataTypesState(
183 BackendDataTypeConfigurer::CRYPTO, crypto_types,
185 return config_state_map;
188 void DataTypeManagerImpl::Restart(syncer::ConfigureReason reason) {
189 DVLOG(1) << "Restarting...";
191 // Check for new or resolved data type crypto errors.
192 if (encryption_handler_->IsPassphraseRequired()) {
193 syncer::ModelTypeSet encrypted_types =
194 encryption_handler_->GetEncryptedDataTypes();
195 encrypted_types.RetainAll(last_requested_types_);
196 encrypted_types.RemoveAll(
197 failed_data_types_handler_->GetCryptoErrorTypes());
198 FailedDataTypesHandler::TypeErrorMap crypto_errors =
199 GenerateCryptoErrorsForTypes(encrypted_types);
200 failed_data_types_handler_->UpdateFailedDataTypes(crypto_errors);
202 failed_data_types_handler_->ResetCryptoErrors();
205 syncer::ModelTypeSet failed_types =
206 failed_data_types_handler_->GetFailedTypes();
207 syncer::ModelTypeSet enabled_types =
208 syncer::Difference(last_requested_types_, failed_types);
210 model_association_manager_.Initialize(enabled_types);
211 last_restart_time_ = base::Time::Now();
213 DCHECK(state_ == STOPPED || state_ == CONFIGURED || state_ == RETRYING);
215 // Starting from a "steady state" (stopped or configured) state
216 // should send a start notification.
217 if (state_ == STOPPED || state_ == CONFIGURED)
220 model_association_manager_.StopDisabledTypes();
222 download_types_queue_ = PrioritizeTypes(enabled_types);
223 association_types_queue_ = std::queue<AssociationTypesInfo>();
225 // Tell the backend about the new set of data types we wish to sync.
226 // The task will be invoked when updates are downloaded.
227 state_ = DOWNLOAD_PENDING;
228 configurer_->ConfigureDataTypes(
230 BuildDataTypeConfigStateMap(download_types_queue_.front()),
231 base::Bind(&DataTypeManagerImpl::DownloadReady,
232 weak_ptr_factory_.GetWeakPtr(),
234 download_types_queue_.front(),
235 syncer::ModelTypeSet()),
236 base::Bind(&DataTypeManagerImpl::OnDownloadRetry,
237 weak_ptr_factory_.GetWeakPtr()));
240 syncer::ModelTypeSet DataTypeManagerImpl::GetPriorityTypes() const {
241 syncer::ModelTypeSet high_priority_types;
242 high_priority_types.PutAll(syncer::PriorityCoreTypes());
243 high_priority_types.PutAll(syncer::PriorityUserTypes());
244 return high_priority_types;
247 TypeSetPriorityList DataTypeManagerImpl::PrioritizeTypes(
248 const syncer::ModelTypeSet& types) {
249 syncer::ModelTypeSet high_priority_types = GetPriorityTypes();
250 high_priority_types.RetainAll(types);
252 syncer::ModelTypeSet low_priority_types =
253 syncer::Difference(types, high_priority_types);
255 TypeSetPriorityList result;
256 if (!high_priority_types.Empty())
257 result.push(high_priority_types);
258 if (!low_priority_types.Empty())
259 result.push(low_priority_types);
263 void DataTypeManagerImpl::ProcessReconfigure() {
264 DCHECK(needs_reconfigure_);
266 // Wait for current download and association to finish.
267 if (!(download_types_queue_.empty() && association_types_queue_.empty()))
270 model_association_manager_.ResetForReconfiguration();
272 // An attempt was made to reconfigure while we were already configuring.
273 // This can be because a passphrase was accepted or the user changed the
274 // set of desired types. Either way, |last_requested_types_| will contain
275 // the most recent set of desired types, so we just call configure.
276 // Note: we do this whether or not GetControllersNeedingStart is true,
277 // because we may need to stop datatypes.
278 DVLOG(1) << "Reconfiguring due to previous configure attempt occuring while"
281 // Note: ConfigureImpl is called directly, rather than posted, in order to
282 // ensure that any purging/unapplying/journaling happens while the set of
283 // failed types is still up to date. If stack unwinding were to be done
284 // via PostTask, the failed data types may be reset before the purging was
287 needs_reconfigure_ = false;
288 ConfigureImpl(last_requested_types_, last_configure_reason_);
291 void DataTypeManagerImpl::OnDownloadRetry() {
292 DCHECK(state_ == DOWNLOAD_PENDING || state_ == CONFIGURING);
293 observer_->OnConfigureRetry();
296 void DataTypeManagerImpl::DownloadReady(
297 base::Time download_start_time,
298 syncer::ModelTypeSet types_to_download,
299 syncer::ModelTypeSet high_priority_types_before,
300 syncer::ModelTypeSet first_sync_types,
301 syncer::ModelTypeSet failed_configuration_types) {
302 DCHECK(state_ == DOWNLOAD_PENDING || state_ == CONFIGURING);
304 // Persistence errors are reset after each backend configuration attempt
305 // during which they would have been purged.
306 failed_data_types_handler_->ResetPersistenceErrorsFrom(types_to_download);
308 // Ignore |failed_configuration_types| if we need to reconfigure
310 if (needs_reconfigure_) {
311 download_types_queue_ = TypeSetPriorityList();
312 ProcessReconfigure();
316 if (!failed_configuration_types.Empty()) {
317 ChromeReportUnrecoverableError();
318 std::string error_msg =
319 "Configuration failed for types " +
320 syncer::ModelTypeSetToString(failed_configuration_types);
321 syncer::SyncError error(FROM_HERE,
322 syncer::SyncError::UNRECOVERABLE_ERROR,
324 failed_configuration_types.First().Get());
325 Abort(UNRECOVERABLE_ERROR, error);
329 state_ = CONFIGURING;
331 // Pop and associate download-ready types.
332 syncer::ModelTypeSet ready_types = types_to_download;
333 download_types_queue_.pop();
334 syncer::ModelTypeSet new_types_to_download;
335 if (!download_types_queue_.empty())
336 new_types_to_download = download_types_queue_.front();
338 AssociationTypesInfo association_info;
339 association_info.types = ready_types;
340 association_info.first_sync_types = first_sync_types;
341 association_info.download_start_time = download_start_time;
342 association_info.download_ready_time = base::Time::Now();
343 association_info.high_priority_types_before = high_priority_types_before;
344 association_types_queue_.push(association_info);
345 if (association_types_queue_.size() == 1u)
346 StartNextAssociation();
348 // Download types of low priority while configuring types of high priority.
349 if (!new_types_to_download.Empty()) {
350 configurer_->ConfigureDataTypes(
351 last_configure_reason_,
352 BuildDataTypeConfigStateMap(new_types_to_download),
353 base::Bind(&DataTypeManagerImpl::DownloadReady,
354 weak_ptr_factory_.GetWeakPtr(),
356 new_types_to_download,
357 syncer::Union(ready_types, high_priority_types_before)),
358 base::Bind(&DataTypeManagerImpl::OnDownloadRetry,
359 weak_ptr_factory_.GetWeakPtr()));
363 void DataTypeManagerImpl::StartNextAssociation() {
364 CHECK(!association_types_queue_.empty());
366 association_types_queue_.front().association_request_time =
368 model_association_manager_.StartAssociationAsync(
369 association_types_queue_.front().types);
372 void DataTypeManagerImpl::OnSingleDataTypeAssociationDone(
373 syncer::ModelType type,
374 const syncer::DataTypeAssociationStats& association_stats) {
375 DCHECK(!association_types_queue_.empty());
377 if (!debug_info_listener_.IsInitialized())
380 configuration_stats_.push_back(syncer::DataTypeConfigurationStats());
381 configuration_stats_.back().model_type = type;
382 configuration_stats_.back().association_stats = association_stats;
384 AssociationTypesInfo& info = association_types_queue_.front();
385 configuration_stats_.back().download_wait_time =
386 info.download_start_time - last_restart_time_;
387 if (info.first_sync_types.Has(type)) {
388 configuration_stats_.back().download_time =
389 info.download_ready_time - info.download_start_time;
391 configuration_stats_.back().association_wait_time_for_high_priority =
392 info.association_request_time - info.download_ready_time;
393 configuration_stats_.back().high_priority_types_configured_before =
394 info.high_priority_types_before;
395 configuration_stats_.back().same_priority_types_configured_before =
396 info.configured_types;
398 info.configured_types.Put(type);
401 void DataTypeManagerImpl::OnModelAssociationDone(
402 const DataTypeManager::ConfigureResult& result) {
403 DCHECK(state_ == STOPPING || state_ == CONFIGURING);
405 if (state_ == STOPPING)
408 // Don't reconfigure due to failed data types if we have an unrecoverable
409 // error or have already aborted.
410 if (result.status == PARTIAL_SUCCESS) {
411 if (!result.needs_crypto.Empty()) {
412 needs_reconfigure_ = true;
413 syncer::ModelTypeSet encrypted_types = result.needs_crypto;
414 encrypted_types.RemoveAll(
415 failed_data_types_handler_->GetCryptoErrorTypes());
416 FailedDataTypesHandler::TypeErrorMap crypto_errors =
417 GenerateCryptoErrorsForTypes(encrypted_types);
418 failed_data_types_handler_->UpdateFailedDataTypes(crypto_errors);
420 if (!result.failed_data_types.empty()) {
421 needs_reconfigure_ = true;
422 failed_data_types_handler_->UpdateFailedDataTypes(
423 result.failed_data_types);
427 // Ignore abort/unrecoverable error if we need to reconfigure anyways.
428 if (needs_reconfigure_) {
429 association_types_queue_ = std::queue<AssociationTypesInfo>();
430 ProcessReconfigure();
434 if (result.status == ABORTED || result.status == UNRECOVERABLE_ERROR) {
435 Abort(result.status, result.failed_data_types.size() >= 1 ?
436 result.failed_data_types.begin()->second :
437 syncer::SyncError());
441 DCHECK(result.status == PARTIAL_SUCCESS || result.status == OK);
442 DCHECK(result.status != OK ||
443 (result.needs_crypto.Empty() && result.failed_data_types.empty()));
445 // It's possible this is a retry to disable failed types, in which case
446 // the association would be SUCCESS, but the overall configuration should
447 // still be PARTIAL_SUCCESS.
448 syncer::ModelTypeSet failed_data_types =
449 failed_data_types_handler_->GetFailedTypes();
450 ConfigureStatus status = result.status;
451 if (!syncer::Intersection(last_requested_types_,
452 failed_data_types).Empty() && result.status == OK) {
453 status = PARTIAL_SUCCESS;
456 association_types_queue_.pop();
457 if (!association_types_queue_.empty()) {
458 StartNextAssociation();
459 } else if (download_types_queue_.empty()) {
461 ConfigureResult configure_result(status,
462 result.requested_types,
463 failed_data_types_handler_->GetAllErrors(),
464 result.waiting_to_start,
465 result.needs_crypto);
466 NotifyDone(configure_result);
470 void DataTypeManagerImpl::OnTypesLoaded() {
471 if (state_ != CONFIGURED) {
472 // Ignore this. either we just started another configuration or
473 // we are in some sort of error.
477 Restart(syncer::CONFIGURE_REASON_RECONFIGURATION);
481 void DataTypeManagerImpl::Stop() {
482 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
483 if (state_ == STOPPED)
486 bool need_to_notify =
487 state_ == DOWNLOAD_PENDING || state_ == CONFIGURING;
490 if (need_to_notify) {
491 ConfigureResult result(ABORTED,
492 last_requested_types_,
493 std::map<syncer::ModelType, syncer::SyncError>(),
494 syncer::ModelTypeSet(),
495 syncer::ModelTypeSet());
500 void DataTypeManagerImpl::Abort(ConfigureStatus status,
501 const syncer::SyncError& error) {
502 DCHECK(state_ == DOWNLOAD_PENDING || state_ == CONFIGURING);
506 DCHECK_NE(OK, status);
507 std::map<syncer::ModelType, syncer::SyncError> errors;
509 errors[error.model_type()] = error;
510 ConfigureResult result(status,
511 last_requested_types_,
513 syncer::ModelTypeSet(),
514 syncer::ModelTypeSet());
518 void DataTypeManagerImpl::StopImpl() {
521 // Invalidate weak pointer to drop download callbacks.
522 weak_ptr_factory_.InvalidateWeakPtrs();
524 // Stop all data types. This may trigger association callback but the
525 // callback will do nothing because state is set to STOPPING above.
526 model_association_manager_.Stop();
531 void DataTypeManagerImpl::NotifyStart() {
532 observer_->OnConfigureStart();
535 void DataTypeManagerImpl::NotifyDone(const ConfigureResult& result) {
536 AddToConfigureTime();
538 DVLOG(1) << "Total time spent configuring: "
539 << configure_time_delta_.InSecondsF() << "s";
540 switch (result.status) {
541 case DataTypeManager::OK:
542 DVLOG(1) << "NotifyDone called with result: OK";
543 UMA_HISTOGRAM_LONG_TIMES("Sync.ConfigureTime_Long.OK",
544 configure_time_delta_);
545 if (debug_info_listener_.IsInitialized() &&
546 !configuration_stats_.empty()) {
547 debug_info_listener_.Call(
549 &syncer::DataTypeDebugInfoListener::OnDataTypeConfigureComplete,
550 configuration_stats_);
552 configuration_stats_.clear();
554 case DataTypeManager::ABORTED:
555 DVLOG(1) << "NotifyDone called with result: ABORTED";
556 UMA_HISTOGRAM_LONG_TIMES("Sync.ConfigureTime_Long.ABORTED",
557 configure_time_delta_);
559 case DataTypeManager::UNRECOVERABLE_ERROR:
560 DVLOG(1) << "NotifyDone called with result: UNRECOVERABLE_ERROR";
561 UMA_HISTOGRAM_LONG_TIMES("Sync.ConfigureTime_Long.UNRECOVERABLE_ERROR",
562 configure_time_delta_);
564 case DataTypeManager::PARTIAL_SUCCESS:
565 DVLOG(1) << "NotifyDone called with result: PARTIAL_SUCCESS";
566 UMA_HISTOGRAM_LONG_TIMES("Sync.ConfigureTime_Long.PARTIAL_SUCCESS",
567 configure_time_delta_);
573 observer_->OnConfigureDone(result);
576 DataTypeManager::State DataTypeManagerImpl::state() const {
580 void DataTypeManagerImpl::AddToConfigureTime() {
581 DCHECK(!last_restart_time_.is_null());
582 configure_time_delta_ += (base::Time::Now() - last_restart_time_);
585 } // namespace browser_sync