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