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 "chrome/browser/sync/glue/data_type_manager_impl.h" 6 7 #include <algorithm> 8 #include <functional> 9 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" 26 27 using content::BrowserThread; 28 29 namespace browser_sync { 30 31 namespace { 32 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( 39 FROM_HERE, 40 syncer::SyncError::CRYPTO_ERROR, 41 "", 42 iter.Get()); 43 } 44 return crypto_errors; 45 } 46 47 } // namespace 48 49 DataTypeManagerImpl::AssociationTypesInfo::AssociationTypesInfo() {} 50 DataTypeManagerImpl::AssociationTypesInfo::~AssociationTypesInfo() {} 51 52 DataTypeManagerImpl::DataTypeManagerImpl( 53 const syncer::WeakHandle<syncer::DataTypeDebugInfoListener>& 54 debug_info_listener, 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 weak_ptr_factory_(this), 66 debug_info_listener_(debug_info_listener), 67 model_association_manager_(controllers, this), 68 observer_(observer), 69 failed_data_types_handler_(failed_data_types_handler), 70 encryption_handler_(encryption_handler) { 71 DCHECK(failed_data_types_handler_); 72 DCHECK(configurer_); 73 DCHECK(observer_); 74 } 75 76 DataTypeManagerImpl::~DataTypeManagerImpl() {} 77 78 void DataTypeManagerImpl::Configure(syncer::ModelTypeSet desired_types, 79 syncer::ConfigureReason reason) { 80 desired_types.PutAll(syncer::CoreTypes()); 81 ConfigureImpl(desired_types, reason); 82 } 83 84 void DataTypeManagerImpl::PurgeForMigration( 85 syncer::ModelTypeSet undesired_types, 86 syncer::ConfigureReason reason) { 87 syncer::ModelTypeSet remainder = Difference(last_requested_types_, 88 undesired_types); 89 ConfigureImpl(remainder, reason); 90 } 91 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."; 102 return; 103 } 104 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."; 113 NotifyStart(); 114 ConfigureResult result(OK, last_requested_types_); 115 NotifyDone(result); 116 return; 117 } 118 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; 126 return; 127 } 128 129 Restart(reason); 130 } 131 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(); 147 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); 153 154 syncer::ModelTypeSet enabled_types = last_requested_types_; 155 enabled_types.RemoveAll(error_types); 156 syncer::ModelTypeSet disabled_types = 157 syncer::Difference( 158 syncer::Union(syncer::UserTypes(), syncer::ControlTypes()), 159 enabled_types); 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); 165 166 BackendDataTypeConfigurer::DataTypeConfigStateMap config_state_map; 167 BackendDataTypeConfigurer::SetDataTypesState( 168 BackendDataTypeConfigurer::CONFIGURE_INACTIVE, enabled_types, 169 &config_state_map); 170 BackendDataTypeConfigurer::SetDataTypesState( 171 BackendDataTypeConfigurer::CONFIGURE_ACTIVE, to_configure, 172 &config_state_map); 173 BackendDataTypeConfigurer::SetDataTypesState( 174 BackendDataTypeConfigurer::CONFIGURE_CLEAN, persistence_types, 175 &config_state_map); 176 BackendDataTypeConfigurer::SetDataTypesState( 177 BackendDataTypeConfigurer::DISABLED, disabled_types, 178 &config_state_map); 179 BackendDataTypeConfigurer::SetDataTypesState( 180 BackendDataTypeConfigurer::FATAL, fatal_types, 181 &config_state_map); 182 BackendDataTypeConfigurer::SetDataTypesState( 183 BackendDataTypeConfigurer::CRYPTO, crypto_types, 184 &config_state_map); 185 return config_state_map; 186 } 187 188 void DataTypeManagerImpl::Restart(syncer::ConfigureReason reason) { 189 DVLOG(1) << "Restarting..."; 190 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); 201 } else { 202 failed_data_types_handler_->ResetCryptoErrors(); 203 } 204 205 syncer::ModelTypeSet failed_types = 206 failed_data_types_handler_->GetFailedTypes(); 207 syncer::ModelTypeSet enabled_types = 208 syncer::Difference(last_requested_types_, failed_types); 209 210 model_association_manager_.Initialize(enabled_types); 211 last_restart_time_ = base::Time::Now(); 212 213 DCHECK(state_ == STOPPED || state_ == CONFIGURED || state_ == RETRYING); 214 215 // Starting from a "steady state" (stopped or configured) state 216 // should send a start notification. 217 if (state_ == STOPPED || state_ == CONFIGURED) 218 NotifyStart(); 219 220 model_association_manager_.StopDisabledTypes(); 221 222 download_types_queue_ = PrioritizeTypes(enabled_types); 223 association_types_queue_ = std::queue<AssociationTypesInfo>(); 224 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( 229 reason, 230 BuildDataTypeConfigStateMap(download_types_queue_.front()), 231 base::Bind(&DataTypeManagerImpl::DownloadReady, 232 weak_ptr_factory_.GetWeakPtr(), 233 base::Time::Now(), 234 download_types_queue_.front(), 235 syncer::ModelTypeSet()), 236 base::Bind(&DataTypeManagerImpl::OnDownloadRetry, 237 weak_ptr_factory_.GetWeakPtr())); 238 } 239 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; 245 } 246 247 TypeSetPriorityList DataTypeManagerImpl::PrioritizeTypes( 248 const syncer::ModelTypeSet& types) { 249 syncer::ModelTypeSet high_priority_types = GetPriorityTypes(); 250 high_priority_types.RetainAll(types); 251 252 syncer::ModelTypeSet low_priority_types = 253 syncer::Difference(types, high_priority_types); 254 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); 260 return result; 261 } 262 263 void DataTypeManagerImpl::ProcessReconfigure() { 264 DCHECK(needs_reconfigure_); 265 266 // Wait for current download and association to finish. 267 if (!(download_types_queue_.empty() && association_types_queue_.empty())) 268 return; 269 270 model_association_manager_.ResetForReconfiguration(); 271 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" 279 << " busy."; 280 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 285 // performed. 286 state_ = RETRYING; 287 needs_reconfigure_ = false; 288 ConfigureImpl(last_requested_types_, last_configure_reason_); 289 } 290 291 void DataTypeManagerImpl::OnDownloadRetry() { 292 DCHECK(state_ == DOWNLOAD_PENDING || state_ == CONFIGURING); 293 observer_->OnConfigureRetry(); 294 } 295 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); 303 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); 307 308 // Ignore |failed_configuration_types| if we need to reconfigure 309 // anyway. 310 if (needs_reconfigure_) { 311 download_types_queue_ = TypeSetPriorityList(); 312 ProcessReconfigure(); 313 return; 314 } 315 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, 323 error_msg, 324 failed_configuration_types.First().Get()); 325 Abort(UNRECOVERABLE_ERROR, error); 326 return; 327 } 328 329 state_ = CONFIGURING; 330 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(); 337 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(); 347 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(), 355 base::Time::Now(), 356 new_types_to_download, 357 syncer::Union(ready_types, high_priority_types_before)), 358 base::Bind(&DataTypeManagerImpl::OnDownloadRetry, 359 weak_ptr_factory_.GetWeakPtr())); 360 } 361 } 362 363 void DataTypeManagerImpl::StartNextAssociation() { 364 CHECK(!association_types_queue_.empty()); 365 366 association_types_queue_.front().association_request_time = 367 base::Time::Now(); 368 model_association_manager_.StartAssociationAsync( 369 association_types_queue_.front().types); 370 } 371 372 void DataTypeManagerImpl::OnSingleDataTypeAssociationDone( 373 syncer::ModelType type, 374 const syncer::DataTypeAssociationStats& association_stats) { 375 DCHECK(!association_types_queue_.empty()); 376 377 if (!debug_info_listener_.IsInitialized()) 378 return; 379 380 syncer::DataTypeConfigurationStats configuration_stats; 381 configuration_stats.model_type = type; 382 configuration_stats.association_stats = association_stats; 383 384 AssociationTypesInfo& info = association_types_queue_.front(); 385 configuration_stats.download_wait_time = 386 info.download_start_time - last_restart_time_; 387 if (info.first_sync_types.Has(type)) { 388 configuration_stats.download_time = 389 info.download_ready_time - info.download_start_time; 390 } 391 configuration_stats.association_wait_time_for_high_priority = 392 info.association_request_time - info.download_ready_time; 393 configuration_stats.high_priority_types_configured_before = 394 info.high_priority_types_before; 395 configuration_stats.same_priority_types_configured_before = 396 info.configured_types; 397 398 info.configured_types.Put(type); 399 400 debug_info_listener_.Call( 401 FROM_HERE, 402 &syncer::DataTypeDebugInfoListener::OnSingleDataTypeConfigureComplete, 403 configuration_stats); 404 } 405 406 void DataTypeManagerImpl::OnModelAssociationDone( 407 const DataTypeManager::ConfigureResult& result) { 408 DCHECK(state_ == STOPPING || state_ == CONFIGURING); 409 410 if (state_ == STOPPING) 411 return; 412 413 // Don't reconfigure due to failed data types if we have an unrecoverable 414 // error or have already aborted. 415 if (result.status == PARTIAL_SUCCESS) { 416 if (!result.needs_crypto.Empty()) { 417 needs_reconfigure_ = true; 418 syncer::ModelTypeSet encrypted_types = result.needs_crypto; 419 encrypted_types.RemoveAll( 420 failed_data_types_handler_->GetCryptoErrorTypes()); 421 FailedDataTypesHandler::TypeErrorMap crypto_errors = 422 GenerateCryptoErrorsForTypes(encrypted_types); 423 failed_data_types_handler_->UpdateFailedDataTypes(crypto_errors); 424 } 425 if (!result.failed_data_types.empty()) { 426 needs_reconfigure_ = true; 427 failed_data_types_handler_->UpdateFailedDataTypes( 428 result.failed_data_types); 429 } 430 } 431 432 // Ignore abort/unrecoverable error if we need to reconfigure anyways. 433 if (needs_reconfigure_) { 434 association_types_queue_ = std::queue<AssociationTypesInfo>(); 435 ProcessReconfigure(); 436 return; 437 } 438 439 if (result.status == ABORTED || result.status == UNRECOVERABLE_ERROR) { 440 Abort(result.status, result.failed_data_types.size() >= 1 ? 441 result.failed_data_types.begin()->second : 442 syncer::SyncError()); 443 return; 444 } 445 446 DCHECK(result.status == PARTIAL_SUCCESS || result.status == OK); 447 DCHECK(result.status != OK || 448 (result.needs_crypto.Empty() && result.failed_data_types.empty())); 449 450 // It's possible this is a retry to disable failed types, in which case 451 // the association would be SUCCESS, but the overall configuration should 452 // still be PARTIAL_SUCCESS. 453 syncer::ModelTypeSet failed_data_types = 454 failed_data_types_handler_->GetFailedTypes(); 455 ConfigureStatus status = result.status; 456 if (!syncer::Intersection(last_requested_types_, 457 failed_data_types).Empty() && result.status == OK) { 458 status = PARTIAL_SUCCESS; 459 } 460 461 association_types_queue_.pop(); 462 if (!association_types_queue_.empty()) { 463 StartNextAssociation(); 464 } else if (download_types_queue_.empty()) { 465 state_ = CONFIGURED; 466 ConfigureResult configure_result(status, 467 result.requested_types, 468 failed_data_types_handler_->GetAllErrors(), 469 result.waiting_to_start, 470 result.needs_crypto); 471 NotifyDone(configure_result); 472 } 473 } 474 475 void DataTypeManagerImpl::OnTypesLoaded() { 476 if (state_ != CONFIGURED) { 477 // Ignore this. either we just started another configuration or 478 // we are in some sort of error. 479 return; 480 } 481 482 Restart(syncer::CONFIGURE_REASON_RECONFIGURATION); 483 } 484 485 486 void DataTypeManagerImpl::Stop() { 487 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 488 if (state_ == STOPPED) 489 return; 490 491 bool need_to_notify = 492 state_ == DOWNLOAD_PENDING || state_ == CONFIGURING; 493 StopImpl(); 494 495 if (need_to_notify) { 496 ConfigureResult result(ABORTED, 497 last_requested_types_, 498 std::map<syncer::ModelType, syncer::SyncError>(), 499 syncer::ModelTypeSet(), 500 syncer::ModelTypeSet()); 501 NotifyDone(result); 502 } 503 } 504 505 void DataTypeManagerImpl::Abort(ConfigureStatus status, 506 const syncer::SyncError& error) { 507 DCHECK(state_ == DOWNLOAD_PENDING || state_ == CONFIGURING); 508 509 StopImpl(); 510 511 DCHECK_NE(OK, status); 512 std::map<syncer::ModelType, syncer::SyncError> errors; 513 if (error.IsSet()) 514 errors[error.model_type()] = error; 515 ConfigureResult result(status, 516 last_requested_types_, 517 errors, 518 syncer::ModelTypeSet(), 519 syncer::ModelTypeSet()); 520 NotifyDone(result); 521 } 522 523 void DataTypeManagerImpl::StopImpl() { 524 state_ = STOPPING; 525 526 // Invalidate weak pointer to drop download callbacks. 527 weak_ptr_factory_.InvalidateWeakPtrs(); 528 529 // Stop all data types. This may trigger association callback but the 530 // callback will do nothing because state is set to STOPPING above. 531 model_association_manager_.Stop(); 532 533 state_ = STOPPED; 534 } 535 536 void DataTypeManagerImpl::NotifyStart() { 537 observer_->OnConfigureStart(); 538 } 539 540 void DataTypeManagerImpl::NotifyDone(const ConfigureResult& result) { 541 AddToConfigureTime(); 542 543 DVLOG(1) << "Total time spent configuring: " 544 << configure_time_delta_.InSecondsF() << "s"; 545 switch (result.status) { 546 case DataTypeManager::OK: 547 DVLOG(1) << "NotifyDone called with result: OK"; 548 UMA_HISTOGRAM_LONG_TIMES("Sync.ConfigureTime_Long.OK", 549 configure_time_delta_); 550 break; 551 case DataTypeManager::ABORTED: 552 DVLOG(1) << "NotifyDone called with result: ABORTED"; 553 UMA_HISTOGRAM_LONG_TIMES("Sync.ConfigureTime_Long.ABORTED", 554 configure_time_delta_); 555 break; 556 case DataTypeManager::UNRECOVERABLE_ERROR: 557 DVLOG(1) << "NotifyDone called with result: UNRECOVERABLE_ERROR"; 558 UMA_HISTOGRAM_LONG_TIMES("Sync.ConfigureTime_Long.UNRECOVERABLE_ERROR", 559 configure_time_delta_); 560 break; 561 case DataTypeManager::PARTIAL_SUCCESS: 562 DVLOG(1) << "NotifyDone called with result: PARTIAL_SUCCESS"; 563 UMA_HISTOGRAM_LONG_TIMES("Sync.ConfigureTime_Long.PARTIAL_SUCCESS", 564 configure_time_delta_); 565 break; 566 default: 567 NOTREACHED(); 568 break; 569 } 570 observer_->OnConfigureDone(result); 571 } 572 573 DataTypeManager::State DataTypeManagerImpl::state() const { 574 return state_; 575 } 576 577 void DataTypeManagerImpl::AddToConfigureTime() { 578 DCHECK(!last_restart_time_.is_null()); 579 configure_time_delta_ += (base::Time::Now() - last_restart_time_); 580 } 581 582 } // namespace browser_sync 583