- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / sync / glue / data_type_manager_impl.cc
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   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   configuration_stats_.push_back(syncer::DataTypeConfigurationStats());
381   configuration_stats_.back().model_type = type;
382   configuration_stats_.back().association_stats = association_stats;
383
384   AssociationTypesInfo& info = association_types_queue_.front();
385   configuration_stats_.back().download_wait_time =
386       info.download_start_time - last_restart_time_;
387   if (info.first_sync_types.Has(type)) {
388     configuration_stats_.back().download_time =
389         info.download_ready_time - info.download_start_time;
390   }
391   configuration_stats_.back().association_wait_time_for_high_priority =
392       info.association_request_time - info.download_ready_time;
393   configuration_stats_.back().high_priority_types_configured_before =
394       info.high_priority_types_before;
395   configuration_stats_.back().same_priority_types_configured_before =
396       info.configured_types;
397
398   info.configured_types.Put(type);
399 }
400
401 void DataTypeManagerImpl::OnModelAssociationDone(
402     const DataTypeManager::ConfigureResult& result) {
403   DCHECK(state_ == STOPPING || state_ == CONFIGURING);
404
405   if (state_ == STOPPING)
406     return;
407
408   // Don't reconfigure due to failed data types if we have an unrecoverable
409   // error or have already aborted.
410   if (result.status  == PARTIAL_SUCCESS) {
411     if (!result.needs_crypto.Empty()) {
412       needs_reconfigure_ = true;
413       syncer::ModelTypeSet encrypted_types = result.needs_crypto;
414       encrypted_types.RemoveAll(
415           failed_data_types_handler_->GetCryptoErrorTypes());
416       FailedDataTypesHandler::TypeErrorMap crypto_errors =
417           GenerateCryptoErrorsForTypes(encrypted_types);
418       failed_data_types_handler_->UpdateFailedDataTypes(crypto_errors);
419     }
420     if (!result.failed_data_types.empty()) {
421       needs_reconfigure_ = true;
422       failed_data_types_handler_->UpdateFailedDataTypes(
423           result.failed_data_types);
424     }
425   }
426
427   // Ignore abort/unrecoverable error if we need to reconfigure anyways.
428   if (needs_reconfigure_) {
429     association_types_queue_ = std::queue<AssociationTypesInfo>();
430     ProcessReconfigure();
431     return;
432   }
433
434   if (result.status == ABORTED || result.status == UNRECOVERABLE_ERROR) {
435     Abort(result.status, result.failed_data_types.size() >= 1 ?
436                          result.failed_data_types.begin()->second :
437                          syncer::SyncError());
438     return;
439   }
440
441   DCHECK(result.status == PARTIAL_SUCCESS || result.status == OK);
442   DCHECK(result.status != OK ||
443          (result.needs_crypto.Empty() && result.failed_data_types.empty()));
444
445   // It's possible this is a retry to disable failed types, in which case
446   // the association would be SUCCESS, but the overall configuration should
447   // still be PARTIAL_SUCCESS.
448   syncer::ModelTypeSet failed_data_types =
449       failed_data_types_handler_->GetFailedTypes();
450   ConfigureStatus status = result.status;
451   if (!syncer::Intersection(last_requested_types_,
452                             failed_data_types).Empty() && result.status == OK) {
453     status = PARTIAL_SUCCESS;
454   }
455
456   association_types_queue_.pop();
457   if (!association_types_queue_.empty()) {
458     StartNextAssociation();
459   } else if (download_types_queue_.empty()) {
460     state_ = CONFIGURED;
461     ConfigureResult configure_result(status,
462                                      result.requested_types,
463                                      failed_data_types_handler_->GetAllErrors(),
464                                      result.waiting_to_start,
465                                      result.needs_crypto);
466     NotifyDone(configure_result);
467   }
468 }
469
470 void DataTypeManagerImpl::OnTypesLoaded() {
471   if (state_ != CONFIGURED) {
472     // Ignore this. either we just started another configuration or
473     // we are in some sort of error.
474     return;
475   }
476
477   Restart(syncer::CONFIGURE_REASON_RECONFIGURATION);
478 }
479
480
481 void DataTypeManagerImpl::Stop() {
482   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
483   if (state_ == STOPPED)
484     return;
485
486   bool need_to_notify =
487       state_ == DOWNLOAD_PENDING || state_ == CONFIGURING;
488   StopImpl();
489
490   if (need_to_notify) {
491     ConfigureResult result(ABORTED,
492                            last_requested_types_,
493                            std::map<syncer::ModelType, syncer::SyncError>(),
494                            syncer::ModelTypeSet(),
495                            syncer::ModelTypeSet());
496     NotifyDone(result);
497   }
498 }
499
500 void DataTypeManagerImpl::Abort(ConfigureStatus status,
501                                 const syncer::SyncError& error) {
502   DCHECK(state_ == DOWNLOAD_PENDING || state_ == CONFIGURING);
503
504   StopImpl();
505
506   DCHECK_NE(OK, status);
507   std::map<syncer::ModelType, syncer::SyncError> errors;
508   if (error.IsSet())
509     errors[error.model_type()] = error;
510   ConfigureResult result(status,
511                          last_requested_types_,
512                          errors,
513                          syncer::ModelTypeSet(),
514                          syncer::ModelTypeSet());
515   NotifyDone(result);
516 }
517
518 void DataTypeManagerImpl::StopImpl() {
519   state_ = STOPPING;
520
521   // Invalidate weak pointer to drop download callbacks.
522   weak_ptr_factory_.InvalidateWeakPtrs();
523
524   // Stop all data types. This may trigger association callback but the
525   // callback will do nothing because state is set to STOPPING above.
526   model_association_manager_.Stop();
527
528   state_ = STOPPED;
529 }
530
531 void DataTypeManagerImpl::NotifyStart() {
532   observer_->OnConfigureStart();
533 }
534
535 void DataTypeManagerImpl::NotifyDone(const ConfigureResult& result) {
536   AddToConfigureTime();
537
538   DVLOG(1) << "Total time spent configuring: "
539            << configure_time_delta_.InSecondsF() << "s";
540   switch (result.status) {
541     case DataTypeManager::OK:
542       DVLOG(1) << "NotifyDone called with result: OK";
543       UMA_HISTOGRAM_LONG_TIMES("Sync.ConfigureTime_Long.OK",
544                                configure_time_delta_);
545       if (debug_info_listener_.IsInitialized() &&
546           !configuration_stats_.empty()) {
547         debug_info_listener_.Call(
548             FROM_HERE,
549             &syncer::DataTypeDebugInfoListener::OnDataTypeConfigureComplete,
550             configuration_stats_);
551       }
552       configuration_stats_.clear();
553       break;
554     case DataTypeManager::ABORTED:
555       DVLOG(1) << "NotifyDone called with result: ABORTED";
556       UMA_HISTOGRAM_LONG_TIMES("Sync.ConfigureTime_Long.ABORTED",
557                                configure_time_delta_);
558       break;
559     case DataTypeManager::UNRECOVERABLE_ERROR:
560       DVLOG(1) << "NotifyDone called with result: UNRECOVERABLE_ERROR";
561       UMA_HISTOGRAM_LONG_TIMES("Sync.ConfigureTime_Long.UNRECOVERABLE_ERROR",
562                                configure_time_delta_);
563       break;
564     case DataTypeManager::PARTIAL_SUCCESS:
565       DVLOG(1) << "NotifyDone called with result: PARTIAL_SUCCESS";
566       UMA_HISTOGRAM_LONG_TIMES("Sync.ConfigureTime_Long.PARTIAL_SUCCESS",
567                                configure_time_delta_);
568       break;
569     default:
570       NOTREACHED();
571       break;
572   }
573   observer_->OnConfigureDone(result);
574 }
575
576 DataTypeManager::State DataTypeManagerImpl::state() const {
577   return state_;
578 }
579
580 void DataTypeManagerImpl::AddToConfigureTime() {
581   DCHECK(!last_restart_time_.is_null());
582   configure_time_delta_ += (base::Time::Now() - last_restart_time_);
583 }
584
585 }  // namespace browser_sync