Home | History | Annotate | Download | only in sync_driver
      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