1 // Copyright 2014 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_driver/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/metrics/histogram.h" 17 #include "base/strings/stringprintf.h" 18 #include "components/sync_driver/data_type_controller.h" 19 #include "components/sync_driver/data_type_encryption_handler.h" 20 #include "components/sync_driver/data_type_manager_observer.h" 21 #include "components/sync_driver/failed_data_types_handler.h" 22 #include "sync/internal_api/public/data_type_debug_info_listener.h" 23 24 namespace browser_sync { 25 26 namespace { 27 28 FailedDataTypesHandler::TypeErrorMap 29 GenerateCryptoErrorsForTypes(syncer::ModelTypeSet encrypted_types) { 30 FailedDataTypesHandler::TypeErrorMap crypto_errors; 31 for (syncer::ModelTypeSet::Iterator iter = encrypted_types.First(); 32 iter.Good(); iter.Inc()) { 33 crypto_errors[iter.Get()] = syncer::SyncError( 34 FROM_HERE, 35 syncer::SyncError::CRYPTO_ERROR, 36 "", 37 iter.Get()); 38 } 39 return crypto_errors; 40 } 41 42 } // namespace 43 44 DataTypeManagerImpl::AssociationTypesInfo::AssociationTypesInfo() {} 45 DataTypeManagerImpl::AssociationTypesInfo::~AssociationTypesInfo() {} 46 47 DataTypeManagerImpl::DataTypeManagerImpl( 48 const base::Closure& unrecoverable_error_method, 49 const syncer::WeakHandle<syncer::DataTypeDebugInfoListener>& 50 debug_info_listener, 51 const DataTypeController::TypeMap* controllers, 52 const browser_sync::DataTypeEncryptionHandler* encryption_handler, 53 BackendDataTypeConfigurer* configurer, 54 DataTypeManagerObserver* observer, 55 browser_sync::FailedDataTypesHandler* failed_data_types_handler) 56 : configurer_(configurer), 57 controllers_(controllers), 58 state_(DataTypeManager::STOPPED), 59 needs_reconfigure_(false), 60 last_configure_reason_(syncer::CONFIGURE_REASON_UNKNOWN), 61 debug_info_listener_(debug_info_listener), 62 model_association_manager_(controllers, this), 63 observer_(observer), 64 failed_data_types_handler_(failed_data_types_handler), 65 encryption_handler_(encryption_handler), 66 unrecoverable_error_method_(unrecoverable_error_method), 67 weak_ptr_factory_(this) { 68 DCHECK(failed_data_types_handler_); 69 DCHECK(configurer_); 70 DCHECK(observer_); 71 } 72 73 DataTypeManagerImpl::~DataTypeManagerImpl() {} 74 75 void DataTypeManagerImpl::Configure(syncer::ModelTypeSet desired_types, 76 syncer::ConfigureReason reason) { 77 if (reason == syncer::CONFIGURE_REASON_BACKUP_ROLLBACK) 78 desired_types.PutAll(syncer::ControlTypes()); 79 else 80 desired_types.PutAll(syncer::CoreTypes()); 81 82 // Only allow control types and types that have controllers. 83 syncer::ModelTypeSet filtered_desired_types; 84 for (syncer::ModelTypeSet::Iterator type = desired_types.First(); 85 type.Good(); type.Inc()) { 86 DataTypeController::TypeMap::const_iterator iter = 87 controllers_->find(type.Get()); 88 if (syncer::IsControlType(type.Get()) || 89 iter != controllers_->end()) { 90 if (iter != controllers_->end()) { 91 if (!iter->second->ReadyForStart()) { 92 // Add the type to the unready types set to prevent purging it. It's 93 // up to the datatype controller to, if necessary, explicitly 94 // mark the type as broken to trigger a purge. 95 syncer::SyncError error(FROM_HERE, 96 syncer::SyncError::UNREADY_ERROR, 97 "Datatype not ready at config time.", 98 type.Get()); 99 std::map<syncer::ModelType, syncer::SyncError> errors; 100 errors[type.Get()] = error; 101 failed_data_types_handler_->UpdateFailedDataTypes(errors); 102 } else { 103 failed_data_types_handler_->ResetUnreadyErrorFor(type.Get()); 104 } 105 } 106 filtered_desired_types.Put(type.Get()); 107 } 108 } 109 ConfigureImpl(filtered_desired_types, reason); 110 } 111 112 void DataTypeManagerImpl::PurgeForMigration( 113 syncer::ModelTypeSet undesired_types, 114 syncer::ConfigureReason reason) { 115 syncer::ModelTypeSet remainder = Difference(last_requested_types_, 116 undesired_types); 117 ConfigureImpl(remainder, reason); 118 } 119 120 void DataTypeManagerImpl::ConfigureImpl( 121 syncer::ModelTypeSet desired_types, 122 syncer::ConfigureReason reason) { 123 DCHECK_NE(reason, syncer::CONFIGURE_REASON_UNKNOWN); 124 DVLOG(1) << "Configuring for " << syncer::ModelTypeSetToString(desired_types) 125 << " with reason " << reason; 126 if (state_ == STOPPING) { 127 // You can not set a configuration while stopping. 128 LOG(ERROR) << "Configuration set while stopping."; 129 return; 130 } 131 132 // TODO(zea): consider not performing a full configuration once there's a 133 // reliable way to determine if the requested set of enabled types matches the 134 // current set. 135 136 last_requested_types_ = desired_types; 137 last_configure_reason_ = reason; 138 // Only proceed if we're in a steady state or retrying. 139 if (state_ != STOPPED && state_ != CONFIGURED && state_ != RETRYING) { 140 DVLOG(1) << "Received configure request while configuration in flight. " 141 << "Postponing until current configuration complete."; 142 needs_reconfigure_ = true; 143 return; 144 } 145 146 Restart(reason); 147 } 148 149 BackendDataTypeConfigurer::DataTypeConfigStateMap 150 DataTypeManagerImpl::BuildDataTypeConfigStateMap( 151 const syncer::ModelTypeSet& types_being_configured) const { 152 // 1. Get the failed types (due to fatal, crypto, and unready errors). 153 // 2. Add the difference between last_requested_types_ and the failed types 154 // as CONFIGURE_INACTIVE. 155 // 3. Flip |types_being_configured| to CONFIGURE_ACTIVE. 156 // 4. Set non-enabled user types as DISABLED. 157 // 5. Set the fatal, crypto, and unready types to their respective states. 158 syncer::ModelTypeSet error_types = 159 failed_data_types_handler_->GetFailedTypes(); 160 syncer::ModelTypeSet fatal_types = 161 failed_data_types_handler_->GetFatalErrorTypes(); 162 syncer::ModelTypeSet crypto_types = 163 failed_data_types_handler_->GetCryptoErrorTypes(); 164 syncer::ModelTypeSet unready_types= 165 failed_data_types_handler_->GetUnreadyErrorTypes(); 166 167 // Types with persistence errors are only purged/resynced when they're 168 // actively being configured. 169 syncer::ModelTypeSet persistence_types = 170 failed_data_types_handler_->GetPersistenceErrorTypes(); 171 persistence_types.RetainAll(types_being_configured); 172 173 // Types with unready errors do not count as unready if they've been disabled. 174 unready_types.RetainAll(last_requested_types_); 175 176 syncer::ModelTypeSet enabled_types = last_requested_types_; 177 enabled_types.RemoveAll(error_types); 178 syncer::ModelTypeSet disabled_types = 179 syncer::Difference( 180 syncer::Union(syncer::UserTypes(), syncer::ControlTypes()), 181 enabled_types); 182 syncer::ModelTypeSet to_configure = syncer::Intersection( 183 enabled_types, types_being_configured); 184 DVLOG(1) << "Enabling: " << syncer::ModelTypeSetToString(enabled_types); 185 DVLOG(1) << "Configuring: " << syncer::ModelTypeSetToString(to_configure); 186 DVLOG(1) << "Disabling: " << syncer::ModelTypeSetToString(disabled_types); 187 188 BackendDataTypeConfigurer::DataTypeConfigStateMap config_state_map; 189 BackendDataTypeConfigurer::SetDataTypesState( 190 BackendDataTypeConfigurer::CONFIGURE_INACTIVE, enabled_types, 191 &config_state_map); 192 BackendDataTypeConfigurer::SetDataTypesState( 193 BackendDataTypeConfigurer::CONFIGURE_ACTIVE, to_configure, 194 &config_state_map); 195 BackendDataTypeConfigurer::SetDataTypesState( 196 BackendDataTypeConfigurer::CONFIGURE_CLEAN, persistence_types, 197 &config_state_map); 198 BackendDataTypeConfigurer::SetDataTypesState( 199 BackendDataTypeConfigurer::DISABLED, disabled_types, 200 &config_state_map); 201 BackendDataTypeConfigurer::SetDataTypesState( 202 BackendDataTypeConfigurer::FATAL, fatal_types, 203 &config_state_map); 204 BackendDataTypeConfigurer::SetDataTypesState( 205 BackendDataTypeConfigurer::CRYPTO, crypto_types, 206 &config_state_map); 207 BackendDataTypeConfigurer::SetDataTypesState( 208 BackendDataTypeConfigurer::UNREADY, unready_types, 209 &config_state_map); 210 return config_state_map; 211 } 212 213 void DataTypeManagerImpl::Restart(syncer::ConfigureReason reason) { 214 DVLOG(1) << "Restarting..."; 215 216 // Check for new or resolved data type crypto errors. 217 if (encryption_handler_->IsPassphraseRequired()) { 218 syncer::ModelTypeSet encrypted_types = 219 encryption_handler_->GetEncryptedDataTypes(); 220 encrypted_types.RetainAll(last_requested_types_); 221 encrypted_types.RemoveAll( 222 failed_data_types_handler_->GetCryptoErrorTypes()); 223 FailedDataTypesHandler::TypeErrorMap crypto_errors = 224 GenerateCryptoErrorsForTypes(encrypted_types); 225 failed_data_types_handler_->UpdateFailedDataTypes(crypto_errors); 226 } else { 227 failed_data_types_handler_->ResetCryptoErrors(); 228 } 229 230 syncer::ModelTypeSet failed_types = 231 failed_data_types_handler_->GetFailedTypes(); 232 syncer::ModelTypeSet enabled_types = 233 syncer::Difference(last_requested_types_, failed_types); 234 235 last_restart_time_ = base::Time::Now(); 236 configuration_stats_.clear(); 237 238 DCHECK(state_ == STOPPED || state_ == CONFIGURED || state_ == RETRYING); 239 240 // Starting from a "steady state" (stopped or configured) state 241 // should send a start notification. 242 if (state_ == STOPPED || state_ == CONFIGURED) 243 NotifyStart(); 244 245 model_association_manager_.Initialize(enabled_types); 246 247 download_types_queue_ = PrioritizeTypes(enabled_types); 248 association_types_queue_ = std::queue<AssociationTypesInfo>(); 249 250 // Tell the backend about the new set of data types we wish to sync. 251 // The task will be invoked when updates are downloaded. 252 state_ = DOWNLOAD_PENDING; 253 configurer_->ConfigureDataTypes( 254 reason, 255 BuildDataTypeConfigStateMap(download_types_queue_.front()), 256 base::Bind(&DataTypeManagerImpl::DownloadReady, 257 weak_ptr_factory_.GetWeakPtr(), 258 base::Time::Now(), 259 download_types_queue_.front(), 260 syncer::ModelTypeSet()), 261 base::Bind(&DataTypeManagerImpl::OnDownloadRetry, 262 weak_ptr_factory_.GetWeakPtr())); 263 } 264 265 syncer::ModelTypeSet DataTypeManagerImpl::GetPriorityTypes() const { 266 syncer::ModelTypeSet high_priority_types; 267 high_priority_types.PutAll(syncer::PriorityCoreTypes()); 268 high_priority_types.PutAll(syncer::PriorityUserTypes()); 269 return high_priority_types; 270 } 271 272 TypeSetPriorityList DataTypeManagerImpl::PrioritizeTypes( 273 const syncer::ModelTypeSet& types) { 274 syncer::ModelTypeSet high_priority_types = GetPriorityTypes(); 275 high_priority_types.RetainAll(types); 276 277 syncer::ModelTypeSet low_priority_types = 278 syncer::Difference(types, high_priority_types); 279 280 TypeSetPriorityList result; 281 if (!high_priority_types.Empty()) 282 result.push(high_priority_types); 283 if (!low_priority_types.Empty()) 284 result.push(low_priority_types); 285 286 // Could be empty in case of purging for migration, sync nothing, etc. 287 // Configure empty set to purge data from backend. 288 if (result.empty()) 289 result.push(syncer::ModelTypeSet()); 290 291 return result; 292 } 293 294 void DataTypeManagerImpl::ProcessReconfigure() { 295 DCHECK(needs_reconfigure_); 296 297 // Wait for current download and association to finish. 298 if (!(download_types_queue_.empty() && association_types_queue_.empty())) 299 return; 300 301 // An attempt was made to reconfigure while we were already configuring. 302 // This can be because a passphrase was accepted or the user changed the 303 // set of desired types. Either way, |last_requested_types_| will contain 304 // the most recent set of desired types, so we just call configure. 305 // Note: we do this whether or not GetControllersNeedingStart is true, 306 // because we may need to stop datatypes. 307 DVLOG(1) << "Reconfiguring due to previous configure attempt occuring while" 308 << " busy."; 309 310 // Note: ConfigureImpl is called directly, rather than posted, in order to 311 // ensure that any purging/unapplying/journaling happens while the set of 312 // failed types is still up to date. If stack unwinding were to be done 313 // via PostTask, the failed data types may be reset before the purging was 314 // performed. 315 state_ = RETRYING; 316 needs_reconfigure_ = false; 317 ConfigureImpl(last_requested_types_, last_configure_reason_); 318 } 319 320 void DataTypeManagerImpl::OnDownloadRetry() { 321 DCHECK(state_ == DOWNLOAD_PENDING || state_ == CONFIGURING); 322 observer_->OnConfigureRetry(); 323 } 324 325 void DataTypeManagerImpl::DownloadReady( 326 base::Time download_start_time, 327 syncer::ModelTypeSet types_to_download, 328 syncer::ModelTypeSet high_priority_types_before, 329 syncer::ModelTypeSet first_sync_types, 330 syncer::ModelTypeSet failed_configuration_types) { 331 DCHECK(state_ == DOWNLOAD_PENDING || state_ == CONFIGURING); 332 333 // Persistence errors are reset after each backend configuration attempt 334 // during which they would have been purged. 335 failed_data_types_handler_->ResetPersistenceErrorsFrom(types_to_download); 336 337 // Ignore |failed_configuration_types| if we need to reconfigure 338 // anyway. 339 if (needs_reconfigure_) { 340 download_types_queue_ = TypeSetPriorityList(); 341 ProcessReconfigure(); 342 return; 343 } 344 345 if (!failed_configuration_types.Empty()) { 346 if (!unrecoverable_error_method_.is_null()) 347 unrecoverable_error_method_.Run(); 348 std::string error_msg = 349 "Configuration failed for types " + 350 syncer::ModelTypeSetToString(failed_configuration_types); 351 syncer::SyncError error(FROM_HERE, 352 syncer::SyncError::UNRECOVERABLE_ERROR, 353 error_msg, 354 failed_configuration_types.First().Get()); 355 Abort(UNRECOVERABLE_ERROR, error); 356 return; 357 } 358 359 state_ = CONFIGURING; 360 361 // Pop and associate download-ready types. 362 syncer::ModelTypeSet ready_types = types_to_download; 363 download_types_queue_.pop(); 364 syncer::ModelTypeSet new_types_to_download; 365 if (!download_types_queue_.empty()) 366 new_types_to_download = download_types_queue_.front(); 367 368 AssociationTypesInfo association_info; 369 association_info.types = ready_types; 370 association_info.first_sync_types = first_sync_types; 371 association_info.download_start_time = download_start_time; 372 association_info.download_ready_time = base::Time::Now(); 373 association_info.high_priority_types_before = high_priority_types_before; 374 association_types_queue_.push(association_info); 375 if (association_types_queue_.size() == 1u) 376 StartNextAssociation(); 377 378 // Download types of low priority while configuring types of high priority. 379 if (!new_types_to_download.Empty()) { 380 configurer_->ConfigureDataTypes( 381 last_configure_reason_, 382 BuildDataTypeConfigStateMap(new_types_to_download), 383 base::Bind(&DataTypeManagerImpl::DownloadReady, 384 weak_ptr_factory_.GetWeakPtr(), 385 base::Time::Now(), 386 new_types_to_download, 387 syncer::Union(ready_types, high_priority_types_before)), 388 base::Bind(&DataTypeManagerImpl::OnDownloadRetry, 389 weak_ptr_factory_.GetWeakPtr())); 390 } 391 } 392 393 void DataTypeManagerImpl::StartNextAssociation() { 394 CHECK(!association_types_queue_.empty()); 395 396 association_types_queue_.front().association_request_time = 397 base::Time::Now(); 398 model_association_manager_.StartAssociationAsync( 399 association_types_queue_.front().types); 400 } 401 402 void DataTypeManagerImpl::OnSingleDataTypeWillStop( 403 syncer::ModelType type) { 404 configurer_->DeactivateDataType(type); 405 } 406 407 void DataTypeManagerImpl::OnSingleDataTypeAssociationDone( 408 syncer::ModelType type, 409 const syncer::DataTypeAssociationStats& association_stats) { 410 DCHECK(!association_types_queue_.empty()); 411 DataTypeController::TypeMap::const_iterator c_it = controllers_->find(type); 412 DCHECK(c_it != controllers_->end()); 413 if (c_it->second->state() == DataTypeController::RUNNING) { 414 // Tell the backend about the change processor for this type so it can 415 // begin routing changes to it. 416 configurer_->ActivateDataType(type, c_it->second->model_safe_group(), 417 c_it->second->GetChangeProcessor()); 418 } 419 420 if (!debug_info_listener_.IsInitialized()) 421 return; 422 423 AssociationTypesInfo& info = association_types_queue_.front(); 424 configuration_stats_.push_back(syncer::DataTypeConfigurationStats()); 425 configuration_stats_.back().model_type = type; 426 configuration_stats_.back().association_stats = association_stats; 427 if (info.types.Has(type)) { 428 // Times in |info| only apply to non-slow types. 429 configuration_stats_.back().download_wait_time = 430 info.download_start_time - last_restart_time_; 431 if (info.first_sync_types.Has(type)) { 432 configuration_stats_.back().download_time = 433 info.download_ready_time - info.download_start_time; 434 } 435 configuration_stats_.back().association_wait_time_for_high_priority = 436 info.association_request_time - info.download_ready_time; 437 configuration_stats_.back().high_priority_types_configured_before = 438 info.high_priority_types_before; 439 configuration_stats_.back().same_priority_types_configured_before = 440 info.configured_types; 441 info.configured_types.Put(type); 442 } 443 } 444 445 void DataTypeManagerImpl::OnModelAssociationDone( 446 const DataTypeManager::ConfigureResult& result) { 447 DCHECK(state_ == STOPPING || state_ == CONFIGURING); 448 449 if (state_ == STOPPING) 450 return; 451 452 // Don't reconfigure due to failed data types if we have an unrecoverable 453 // error or have already aborted. 454 if (result.status == PARTIAL_SUCCESS) { 455 if (!result.needs_crypto.Empty()) { 456 needs_reconfigure_ = true; 457 syncer::ModelTypeSet encrypted_types = result.needs_crypto; 458 encrypted_types.RemoveAll( 459 failed_data_types_handler_->GetCryptoErrorTypes()); 460 FailedDataTypesHandler::TypeErrorMap crypto_errors = 461 GenerateCryptoErrorsForTypes(encrypted_types); 462 failed_data_types_handler_->UpdateFailedDataTypes(crypto_errors); 463 } 464 if (!result.failed_data_types.empty()) { 465 needs_reconfigure_ = true; 466 failed_data_types_handler_->UpdateFailedDataTypes( 467 result.failed_data_types); 468 } 469 } 470 471 // Ignore abort/unrecoverable error if we need to reconfigure anyways. 472 if (needs_reconfigure_) { 473 association_types_queue_ = std::queue<AssociationTypesInfo>(); 474 ProcessReconfigure(); 475 return; 476 } 477 478 if (result.status == ABORTED || result.status == UNRECOVERABLE_ERROR) { 479 Abort(result.status, result.failed_data_types.size() >= 1 ? 480 result.failed_data_types.begin()->second : 481 syncer::SyncError()); 482 return; 483 } 484 485 DCHECK(result.status == PARTIAL_SUCCESS || result.status == OK); 486 DCHECK(result.status != OK || 487 (result.needs_crypto.Empty() && result.failed_data_types.empty())); 488 489 // It's possible this is a retry to disable failed types, in which case 490 // the association would be SUCCESS, but the overall configuration should 491 // still be PARTIAL_SUCCESS. 492 syncer::ModelTypeSet failed_data_types = 493 failed_data_types_handler_->GetFailedTypes(); 494 ConfigureStatus status = result.status; 495 if (!syncer::Intersection(last_requested_types_, 496 failed_data_types).Empty() && result.status == OK) { 497 status = PARTIAL_SUCCESS; 498 } 499 500 association_types_queue_.pop(); 501 if (!association_types_queue_.empty()) { 502 StartNextAssociation(); 503 } else if (download_types_queue_.empty()) { 504 state_ = CONFIGURED; 505 ConfigureResult configure_result(status, 506 result.requested_types, 507 failed_data_types_handler_->GetAllErrors(), 508 result.unfinished_data_types, 509 result.needs_crypto); 510 NotifyDone(configure_result); 511 } 512 } 513 514 void DataTypeManagerImpl::Stop() { 515 if (state_ == STOPPED) 516 return; 517 518 bool need_to_notify = 519 state_ == DOWNLOAD_PENDING || state_ == CONFIGURING; 520 StopImpl(); 521 522 if (need_to_notify) { 523 ConfigureResult result(ABORTED, 524 last_requested_types_, 525 std::map<syncer::ModelType, syncer::SyncError>(), 526 syncer::ModelTypeSet(), 527 syncer::ModelTypeSet()); 528 NotifyDone(result); 529 } 530 } 531 532 void DataTypeManagerImpl::Abort(ConfigureStatus status, 533 const syncer::SyncError& error) { 534 DCHECK(state_ == DOWNLOAD_PENDING || state_ == CONFIGURING); 535 536 StopImpl(); 537 538 DCHECK_NE(OK, status); 539 std::map<syncer::ModelType, syncer::SyncError> errors; 540 if (error.IsSet()) 541 errors[error.model_type()] = error; 542 ConfigureResult result(status, 543 last_requested_types_, 544 errors, 545 syncer::ModelTypeSet(), 546 syncer::ModelTypeSet()); 547 NotifyDone(result); 548 } 549 550 void DataTypeManagerImpl::StopImpl() { 551 state_ = STOPPING; 552 553 // Invalidate weak pointer to drop download callbacks. 554 weak_ptr_factory_.InvalidateWeakPtrs(); 555 556 // Stop all data types. This may trigger association callback but the 557 // callback will do nothing because state is set to STOPPING above. 558 model_association_manager_.Stop(); 559 560 state_ = STOPPED; 561 } 562 563 void DataTypeManagerImpl::NotifyStart() { 564 observer_->OnConfigureStart(); 565 } 566 567 void DataTypeManagerImpl::NotifyDone(const ConfigureResult& result) { 568 AddToConfigureTime(); 569 570 DVLOG(1) << "Total time spent configuring: " 571 << configure_time_delta_.InSecondsF() << "s"; 572 switch (result.status) { 573 case DataTypeManager::OK: 574 DVLOG(1) << "NotifyDone called with result: OK"; 575 UMA_HISTOGRAM_LONG_TIMES("Sync.ConfigureTime_Long.OK", 576 configure_time_delta_); 577 if (debug_info_listener_.IsInitialized() && 578 !configuration_stats_.empty()) { 579 debug_info_listener_.Call( 580 FROM_HERE, 581 &syncer::DataTypeDebugInfoListener::OnDataTypeConfigureComplete, 582 configuration_stats_); 583 } 584 configuration_stats_.clear(); 585 break; 586 case DataTypeManager::ABORTED: 587 DVLOG(1) << "NotifyDone called with result: ABORTED"; 588 UMA_HISTOGRAM_LONG_TIMES("Sync.ConfigureTime_Long.ABORTED", 589 configure_time_delta_); 590 break; 591 case DataTypeManager::UNRECOVERABLE_ERROR: 592 DVLOG(1) << "NotifyDone called with result: UNRECOVERABLE_ERROR"; 593 UMA_HISTOGRAM_LONG_TIMES("Sync.ConfigureTime_Long.UNRECOVERABLE_ERROR", 594 configure_time_delta_); 595 break; 596 case DataTypeManager::PARTIAL_SUCCESS: 597 DVLOG(1) << "NotifyDone called with result: PARTIAL_SUCCESS"; 598 UMA_HISTOGRAM_LONG_TIMES("Sync.ConfigureTime_Long.PARTIAL_SUCCESS", 599 configure_time_delta_); 600 break; 601 default: 602 NOTREACHED(); 603 break; 604 } 605 observer_->OnConfigureDone(result); 606 } 607 608 DataTypeManager::State DataTypeManagerImpl::state() const { 609 return state_; 610 } 611 612 void DataTypeManagerImpl::AddToConfigureTime() { 613 DCHECK(!last_restart_time_.is_null()); 614 configure_time_delta_ += (base::Time::Now() - last_restart_time_); 615 } 616 617 } // namespace browser_sync 618