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