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 debug_info_listener_(debug_info_listener), 66 model_association_manager_(controllers, this), 67 observer_(observer), 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_); 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 last_restart_time_ = base::Time::Now(); 211 configuration_stats_.clear(); 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_.Initialize(enabled_types); 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 // An attempt was made to reconfigure while we were already configuring. 271 // This can be because a passphrase was accepted or the user changed the 272 // set of desired types. Either way, |last_requested_types_| will contain 273 // the most recent set of desired types, so we just call configure. 274 // Note: we do this whether or not GetControllersNeedingStart is true, 275 // because we may need to stop datatypes. 276 DVLOG(1) << "Reconfiguring due to previous configure attempt occuring while" 277 << " busy."; 278 279 // Note: ConfigureImpl is called directly, rather than posted, in order to 280 // ensure that any purging/unapplying/journaling happens while the set of 281 // failed types is still up to date. If stack unwinding were to be done 282 // via PostTask, the failed data types may be reset before the purging was 283 // performed. 284 state_ = RETRYING; 285 needs_reconfigure_ = false; 286 ConfigureImpl(last_requested_types_, last_configure_reason_); 287 } 288 289 void DataTypeManagerImpl::OnDownloadRetry() { 290 DCHECK(state_ == DOWNLOAD_PENDING || state_ == CONFIGURING); 291 observer_->OnConfigureRetry(); 292 } 293 294 void DataTypeManagerImpl::DownloadReady( 295 base::Time download_start_time, 296 syncer::ModelTypeSet types_to_download, 297 syncer::ModelTypeSet high_priority_types_before, 298 syncer::ModelTypeSet first_sync_types, 299 syncer::ModelTypeSet failed_configuration_types) { 300 DCHECK(state_ == DOWNLOAD_PENDING || state_ == CONFIGURING); 301 302 // Persistence errors are reset after each backend configuration attempt 303 // during which they would have been purged. 304 failed_data_types_handler_->ResetPersistenceErrorsFrom(types_to_download); 305 306 // Ignore |failed_configuration_types| if we need to reconfigure 307 // anyway. 308 if (needs_reconfigure_) { 309 download_types_queue_ = TypeSetPriorityList(); 310 ProcessReconfigure(); 311 return; 312 } 313 314 if (!failed_configuration_types.Empty()) { 315 ChromeReportUnrecoverableError(); 316 std::string error_msg = 317 "Configuration failed for types " + 318 syncer::ModelTypeSetToString(failed_configuration_types); 319 syncer::SyncError error(FROM_HERE, 320 syncer::SyncError::UNRECOVERABLE_ERROR, 321 error_msg, 322 failed_configuration_types.First().Get()); 323 Abort(UNRECOVERABLE_ERROR, error); 324 return; 325 } 326 327 state_ = CONFIGURING; 328 329 // Pop and associate download-ready types. 330 syncer::ModelTypeSet ready_types = types_to_download; 331 download_types_queue_.pop(); 332 syncer::ModelTypeSet new_types_to_download; 333 if (!download_types_queue_.empty()) 334 new_types_to_download = download_types_queue_.front(); 335 336 AssociationTypesInfo association_info; 337 association_info.types = ready_types; 338 association_info.first_sync_types = first_sync_types; 339 association_info.download_start_time = download_start_time; 340 association_info.download_ready_time = base::Time::Now(); 341 association_info.high_priority_types_before = high_priority_types_before; 342 association_types_queue_.push(association_info); 343 if (association_types_queue_.size() == 1u) 344 StartNextAssociation(); 345 346 // Download types of low priority while configuring types of high priority. 347 if (!new_types_to_download.Empty()) { 348 configurer_->ConfigureDataTypes( 349 last_configure_reason_, 350 BuildDataTypeConfigStateMap(new_types_to_download), 351 base::Bind(&DataTypeManagerImpl::DownloadReady, 352 weak_ptr_factory_.GetWeakPtr(), 353 base::Time::Now(), 354 new_types_to_download, 355 syncer::Union(ready_types, high_priority_types_before)), 356 base::Bind(&DataTypeManagerImpl::OnDownloadRetry, 357 weak_ptr_factory_.GetWeakPtr())); 358 } 359 } 360 361 void DataTypeManagerImpl::StartNextAssociation() { 362 CHECK(!association_types_queue_.empty()); 363 364 association_types_queue_.front().association_request_time = 365 base::Time::Now(); 366 model_association_manager_.StartAssociationAsync( 367 association_types_queue_.front().types); 368 } 369 370 void DataTypeManagerImpl::OnSingleDataTypeAssociationDone( 371 syncer::ModelType type, 372 const syncer::DataTypeAssociationStats& association_stats) { 373 DCHECK(!association_types_queue_.empty()); 374 375 if (!debug_info_listener_.IsInitialized()) 376 return; 377 378 AssociationTypesInfo& info = association_types_queue_.front(); 379 configuration_stats_.push_back(syncer::DataTypeConfigurationStats()); 380 configuration_stats_.back().model_type = type; 381 configuration_stats_.back().association_stats = association_stats; 382 if (info.types.Has(type)) { 383 // Times in |info| only apply to non-slow types. 384 configuration_stats_.back().download_wait_time = 385 info.download_start_time - last_restart_time_; 386 if (info.first_sync_types.Has(type)) { 387 configuration_stats_.back().download_time = 388 info.download_ready_time - info.download_start_time; 389 } 390 configuration_stats_.back().association_wait_time_for_high_priority = 391 info.association_request_time - info.download_ready_time; 392 configuration_stats_.back().high_priority_types_configured_before = 393 info.high_priority_types_before; 394 configuration_stats_.back().same_priority_types_configured_before = 395 info.configured_types; 396 info.configured_types.Put(type); 397 } 398 } 399 400 void DataTypeManagerImpl::OnModelAssociationDone( 401 const DataTypeManager::ConfigureResult& result) { 402 DCHECK(state_ == STOPPING || state_ == CONFIGURING); 403 404 if (state_ == STOPPING) 405 return; 406 407 // Don't reconfigure due to failed data types if we have an unrecoverable 408 // error or have already aborted. 409 if (result.status == PARTIAL_SUCCESS) { 410 if (!result.needs_crypto.Empty()) { 411 needs_reconfigure_ = true; 412 syncer::ModelTypeSet encrypted_types = result.needs_crypto; 413 encrypted_types.RemoveAll( 414 failed_data_types_handler_->GetCryptoErrorTypes()); 415 FailedDataTypesHandler::TypeErrorMap crypto_errors = 416 GenerateCryptoErrorsForTypes(encrypted_types); 417 failed_data_types_handler_->UpdateFailedDataTypes(crypto_errors); 418 } 419 if (!result.failed_data_types.empty()) { 420 needs_reconfigure_ = true; 421 failed_data_types_handler_->UpdateFailedDataTypes( 422 result.failed_data_types); 423 } 424 } 425 426 // Ignore abort/unrecoverable error if we need to reconfigure anyways. 427 if (needs_reconfigure_) { 428 association_types_queue_ = std::queue<AssociationTypesInfo>(); 429 ProcessReconfigure(); 430 return; 431 } 432 433 if (result.status == ABORTED || result.status == UNRECOVERABLE_ERROR) { 434 Abort(result.status, result.failed_data_types.size() >= 1 ? 435 result.failed_data_types.begin()->second : 436 syncer::SyncError()); 437 return; 438 } 439 440 DCHECK(result.status == PARTIAL_SUCCESS || result.status == OK); 441 DCHECK(result.status != OK || 442 (result.needs_crypto.Empty() && result.failed_data_types.empty())); 443 444 // It's possible this is a retry to disable failed types, in which case 445 // the association would be SUCCESS, but the overall configuration should 446 // still be PARTIAL_SUCCESS. 447 syncer::ModelTypeSet failed_data_types = 448 failed_data_types_handler_->GetFailedTypes(); 449 ConfigureStatus status = result.status; 450 if (!syncer::Intersection(last_requested_types_, 451 failed_data_types).Empty() && result.status == OK) { 452 status = PARTIAL_SUCCESS; 453 } 454 455 association_types_queue_.pop(); 456 if (!association_types_queue_.empty()) { 457 StartNextAssociation(); 458 } else if (download_types_queue_.empty()) { 459 state_ = CONFIGURED; 460 ConfigureResult configure_result(status, 461 result.requested_types, 462 failed_data_types_handler_->GetAllErrors(), 463 result.unfinished_data_types, 464 result.needs_crypto); 465 NotifyDone(configure_result); 466 } 467 } 468 469 void DataTypeManagerImpl::Stop() { 470 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 471 if (state_ == STOPPED) 472 return; 473 474 bool need_to_notify = 475 state_ == DOWNLOAD_PENDING || state_ == CONFIGURING; 476 StopImpl(); 477 478 if (need_to_notify) { 479 ConfigureResult result(ABORTED, 480 last_requested_types_, 481 std::map<syncer::ModelType, syncer::SyncError>(), 482 syncer::ModelTypeSet(), 483 syncer::ModelTypeSet()); 484 NotifyDone(result); 485 } 486 } 487 488 void DataTypeManagerImpl::Abort(ConfigureStatus status, 489 const syncer::SyncError& error) { 490 DCHECK(state_ == DOWNLOAD_PENDING || state_ == CONFIGURING); 491 492 StopImpl(); 493 494 DCHECK_NE(OK, status); 495 std::map<syncer::ModelType, syncer::SyncError> errors; 496 if (error.IsSet()) 497 errors[error.model_type()] = error; 498 ConfigureResult result(status, 499 last_requested_types_, 500 errors, 501 syncer::ModelTypeSet(), 502 syncer::ModelTypeSet()); 503 NotifyDone(result); 504 } 505 506 void DataTypeManagerImpl::StopImpl() { 507 state_ = STOPPING; 508 509 // Invalidate weak pointer to drop download callbacks. 510 weak_ptr_factory_.InvalidateWeakPtrs(); 511 512 // Stop all data types. This may trigger association callback but the 513 // callback will do nothing because state is set to STOPPING above. 514 model_association_manager_.Stop(); 515 516 state_ = STOPPED; 517 } 518 519 void DataTypeManagerImpl::NotifyStart() { 520 observer_->OnConfigureStart(); 521 } 522 523 void DataTypeManagerImpl::NotifyDone(const ConfigureResult& result) { 524 AddToConfigureTime(); 525 526 DVLOG(1) << "Total time spent configuring: " 527 << configure_time_delta_.InSecondsF() << "s"; 528 switch (result.status) { 529 case DataTypeManager::OK: 530 DVLOG(1) << "NotifyDone called with result: OK"; 531 UMA_HISTOGRAM_LONG_TIMES("Sync.ConfigureTime_Long.OK", 532 configure_time_delta_); 533 if (debug_info_listener_.IsInitialized() && 534 !configuration_stats_.empty()) { 535 debug_info_listener_.Call( 536 FROM_HERE, 537 &syncer::DataTypeDebugInfoListener::OnDataTypeConfigureComplete, 538 configuration_stats_); 539 } 540 configuration_stats_.clear(); 541 break; 542 case DataTypeManager::ABORTED: 543 DVLOG(1) << "NotifyDone called with result: ABORTED"; 544 UMA_HISTOGRAM_LONG_TIMES("Sync.ConfigureTime_Long.ABORTED", 545 configure_time_delta_); 546 break; 547 case DataTypeManager::UNRECOVERABLE_ERROR: 548 DVLOG(1) << "NotifyDone called with result: UNRECOVERABLE_ERROR"; 549 UMA_HISTOGRAM_LONG_TIMES("Sync.ConfigureTime_Long.UNRECOVERABLE_ERROR", 550 configure_time_delta_); 551 break; 552 case DataTypeManager::PARTIAL_SUCCESS: 553 DVLOG(1) << "NotifyDone called with result: PARTIAL_SUCCESS"; 554 UMA_HISTOGRAM_LONG_TIMES("Sync.ConfigureTime_Long.PARTIAL_SUCCESS", 555 configure_time_delta_); 556 break; 557 default: 558 NOTREACHED(); 559 break; 560 } 561 observer_->OnConfigureDone(result); 562 } 563 564 DataTypeManager::State DataTypeManagerImpl::state() const { 565 return state_; 566 } 567 568 void DataTypeManagerImpl::AddToConfigureTime() { 569 DCHECK(!last_restart_time_.is_null()); 570 configure_time_delta_ += (base::Time::Now() - last_restart_time_); 571 } 572 573 } // namespace browser_sync 574