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