| OLD | NEW |
| (Empty) |
| 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/model_association_manager.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 #include <functional> | |
| 9 | |
| 10 #include "base/debug/trace_event.h" | |
| 11 #include "base/logging.h" | |
| 12 #include "base/message_loop/message_loop.h" | |
| 13 #include "base/metrics/histogram.h" | |
| 14 #include "content/public/browser/browser_thread.h" | |
| 15 #include "sync/internal_api/public/base/model_type.h" | |
| 16 | |
| 17 using content::BrowserThread; | |
| 18 using syncer::ModelTypeSet; | |
| 19 | |
| 20 namespace browser_sync { | |
| 21 | |
| 22 namespace { | |
| 23 | |
| 24 static const syncer::ModelType kStartOrder[] = { | |
| 25 syncer::NIGORI, // Listed for completeness. | |
| 26 syncer::DEVICE_INFO, // Listed for completeness. | |
| 27 syncer::EXPERIMENTS, // Listed for completeness. | |
| 28 syncer::PROXY_TABS, // Listed for completeness. | |
| 29 | |
| 30 // Kick off the association of the non-UI types first so they can associate | |
| 31 // in parallel with the UI types. | |
| 32 syncer::PASSWORDS, | |
| 33 syncer::AUTOFILL, | |
| 34 syncer::AUTOFILL_PROFILE, | |
| 35 syncer::EXTENSION_SETTINGS, | |
| 36 syncer::APP_SETTINGS, | |
| 37 syncer::TYPED_URLS, | |
| 38 syncer::HISTORY_DELETE_DIRECTIVES, | |
| 39 syncer::SYNCED_NOTIFICATIONS, | |
| 40 syncer::SYNCED_NOTIFICATION_APP_INFO, | |
| 41 | |
| 42 // UI thread data types. | |
| 43 syncer::BOOKMARKS, | |
| 44 syncer::MANAGED_USERS, // Syncing managed users on initial login might | |
| 45 // block creating a new managed user, so we | |
| 46 // want to do it early. | |
| 47 syncer::PREFERENCES, | |
| 48 syncer::PRIORITY_PREFERENCES, | |
| 49 syncer::EXTENSIONS, | |
| 50 syncer::APPS, | |
| 51 syncer::APP_LIST, | |
| 52 syncer::THEMES, | |
| 53 syncer::SEARCH_ENGINES, | |
| 54 syncer::SESSIONS, | |
| 55 syncer::APP_NOTIFICATIONS, | |
| 56 syncer::DICTIONARY, | |
| 57 syncer::FAVICON_IMAGES, | |
| 58 syncer::FAVICON_TRACKING, | |
| 59 syncer::MANAGED_USER_SETTINGS, | |
| 60 syncer::MANAGED_USER_SHARED_SETTINGS, | |
| 61 syncer::ARTICLES, | |
| 62 }; | |
| 63 | |
| 64 COMPILE_ASSERT(arraysize(kStartOrder) == | |
| 65 syncer::MODEL_TYPE_COUNT - syncer::FIRST_REAL_MODEL_TYPE, | |
| 66 kStartOrder_IncorrectSize); | |
| 67 | |
| 68 // The amount of time we wait for association to finish. If some types haven't | |
| 69 // finished association by the time, configuration result will be | |
| 70 // PARTIAL_SUCCESS and DataTypeManager is notified of the unfinished types. | |
| 71 const int64 kAssociationTimeOutInSeconds = 600; | |
| 72 | |
| 73 syncer::DataTypeAssociationStats BuildAssociationStatsFromMergeResults( | |
| 74 const syncer::SyncMergeResult& local_merge_result, | |
| 75 const syncer::SyncMergeResult& syncer_merge_result, | |
| 76 const base::TimeDelta& association_wait_time, | |
| 77 const base::TimeDelta& association_time) { | |
| 78 DCHECK_EQ(local_merge_result.model_type(), syncer_merge_result.model_type()); | |
| 79 syncer::DataTypeAssociationStats stats; | |
| 80 stats.had_error = local_merge_result.error().IsSet() || | |
| 81 syncer_merge_result.error().IsSet(); | |
| 82 stats.num_local_items_before_association = | |
| 83 local_merge_result.num_items_before_association(); | |
| 84 stats.num_sync_items_before_association = | |
| 85 syncer_merge_result.num_items_before_association(); | |
| 86 stats.num_local_items_after_association = | |
| 87 local_merge_result.num_items_after_association(); | |
| 88 stats.num_sync_items_after_association = | |
| 89 syncer_merge_result.num_items_after_association(); | |
| 90 stats.num_local_items_added = | |
| 91 local_merge_result.num_items_added(); | |
| 92 stats.num_local_items_deleted = | |
| 93 local_merge_result.num_items_deleted(); | |
| 94 stats.num_local_items_modified = | |
| 95 local_merge_result.num_items_modified(); | |
| 96 stats.local_version_pre_association = | |
| 97 local_merge_result.pre_association_version(); | |
| 98 stats.num_sync_items_added = | |
| 99 syncer_merge_result.num_items_added(); | |
| 100 stats.num_sync_items_deleted = | |
| 101 syncer_merge_result.num_items_deleted(); | |
| 102 stats.num_sync_items_modified = | |
| 103 syncer_merge_result.num_items_modified(); | |
| 104 stats.sync_version_pre_association = | |
| 105 syncer_merge_result.pre_association_version(); | |
| 106 stats.association_wait_time = association_wait_time; | |
| 107 stats.association_time = association_time; | |
| 108 return stats; | |
| 109 } | |
| 110 | |
| 111 } // namespace | |
| 112 | |
| 113 ModelAssociationManager::ModelAssociationManager( | |
| 114 const DataTypeController::TypeMap* controllers, | |
| 115 ModelAssociationResultProcessor* processor) | |
| 116 : state_(IDLE), | |
| 117 controllers_(controllers), | |
| 118 result_processor_(processor), | |
| 119 weak_ptr_factory_(this), | |
| 120 configure_status_(DataTypeManager::UNKNOWN) { | |
| 121 // Ensure all data type controllers are stopped. | |
| 122 for (DataTypeController::TypeMap::const_iterator it = controllers_->begin(); | |
| 123 it != controllers_->end(); ++it) { | |
| 124 DCHECK_EQ(DataTypeController::NOT_RUNNING, (*it).second->state()); | |
| 125 } | |
| 126 } | |
| 127 | |
| 128 ModelAssociationManager::~ModelAssociationManager() { | |
| 129 } | |
| 130 | |
| 131 void ModelAssociationManager::Initialize(syncer::ModelTypeSet desired_types) { | |
| 132 // state_ can be INITIALIZED_TO_CONFIGURE if types are reconfigured when | |
| 133 // data is being downloaded, so StartAssociationAsync() is never called for | |
| 134 // the first configuration. | |
| 135 DCHECK_NE(CONFIGURING, state_); | |
| 136 | |
| 137 // Only keep types that have controllers. | |
| 138 desired_types_.Clear(); | |
| 139 slow_types_.Clear(); | |
| 140 for (syncer::ModelTypeSet::Iterator it = desired_types.First(); | |
| 141 it.Good(); it.Inc()) { | |
| 142 if (controllers_->find(it.Get()) != controllers_->end()) | |
| 143 desired_types_.Put(it.Get()); | |
| 144 } | |
| 145 | |
| 146 DVLOG(1) << "ModelAssociationManager: Initializing for " | |
| 147 << syncer::ModelTypeSetToString(desired_types_); | |
| 148 | |
| 149 state_ = INITIALIZED_TO_CONFIGURE; | |
| 150 | |
| 151 StopDisabledTypes(); | |
| 152 LoadEnabledTypes(); | |
| 153 } | |
| 154 | |
| 155 void ModelAssociationManager::StopDisabledTypes() { | |
| 156 DVLOG(1) << "ModelAssociationManager: Stopping disabled types."; | |
| 157 for (DataTypeController::TypeMap::const_iterator it = controllers_->begin(); | |
| 158 it != controllers_->end(); ++it) { | |
| 159 DataTypeController* dtc = (*it).second.get(); | |
| 160 if (dtc->state() != DataTypeController::NOT_RUNNING && | |
| 161 (!desired_types_.Has(dtc->type()) || | |
| 162 failed_data_types_info_.count(dtc->type()) > 0)) { | |
| 163 DVLOG(1) << "ModelTypeToString: stop " << dtc->name(); | |
| 164 dtc->Stop(); | |
| 165 | |
| 166 loaded_types_.Remove(dtc->type()); | |
| 167 associated_types_.Remove(dtc->type()); | |
| 168 } | |
| 169 } | |
| 170 } | |
| 171 | |
| 172 void ModelAssociationManager::LoadEnabledTypes() { | |
| 173 // Load in kStartOrder. | |
| 174 for (size_t i = 0; i < arraysize(kStartOrder); i++) { | |
| 175 syncer::ModelType type = kStartOrder[i]; | |
| 176 if (!desired_types_.Has(type)) | |
| 177 continue; | |
| 178 | |
| 179 DCHECK(controllers_->find(type) != controllers_->end()); | |
| 180 DataTypeController* dtc = controllers_->find(type)->second.get(); | |
| 181 if (dtc->state() == DataTypeController::NOT_RUNNING) { | |
| 182 DCHECK(!loaded_types_.Has(dtc->type())); | |
| 183 DCHECK(!associated_types_.Has(dtc->type())); | |
| 184 dtc->LoadModels(base::Bind(&ModelAssociationManager::ModelLoadCallback, | |
| 185 weak_ptr_factory_.GetWeakPtr())); | |
| 186 } | |
| 187 } | |
| 188 } | |
| 189 | |
| 190 void ModelAssociationManager::StartAssociationAsync( | |
| 191 const syncer::ModelTypeSet& types_to_associate) { | |
| 192 DCHECK_NE(CONFIGURING, state_); | |
| 193 state_ = CONFIGURING; | |
| 194 | |
| 195 association_start_time_ = base::TimeTicks::Now(); | |
| 196 | |
| 197 requested_types_ = types_to_associate; | |
| 198 | |
| 199 associating_types_ = types_to_associate; | |
| 200 associating_types_.RetainAll(desired_types_); | |
| 201 associating_types_.RemoveAll(associated_types_); | |
| 202 | |
| 203 // Assume success. | |
| 204 configure_status_ = DataTypeManager::OK; | |
| 205 | |
| 206 // Remove types that already failed. | |
| 207 for (std::map<syncer::ModelType, syncer::SyncError>::const_iterator it = | |
| 208 failed_data_types_info_.begin(); | |
| 209 it != failed_data_types_info_.end(); ++it) { | |
| 210 associating_types_.Remove(it->first); | |
| 211 } | |
| 212 | |
| 213 // Done if no types to associate. | |
| 214 if (associating_types_.Empty()) { | |
| 215 ModelAssociationDone(); | |
| 216 return; | |
| 217 } | |
| 218 | |
| 219 timer_.Start(FROM_HERE, | |
| 220 base::TimeDelta::FromSeconds(kAssociationTimeOutInSeconds), | |
| 221 this, | |
| 222 &ModelAssociationManager::ModelAssociationDone); | |
| 223 | |
| 224 // Start association of types that are loaded in specified order. | |
| 225 for (size_t i = 0; i < arraysize(kStartOrder); i++) { | |
| 226 syncer::ModelType type = kStartOrder[i]; | |
| 227 if (!associating_types_.Has(type) || !loaded_types_.Has(type)) | |
| 228 continue; | |
| 229 | |
| 230 DataTypeController* dtc = controllers_->find(type)->second.get(); | |
| 231 DCHECK(DataTypeController::MODEL_LOADED == dtc->state() || | |
| 232 DataTypeController::ASSOCIATING == dtc->state()); | |
| 233 if (dtc->state() == DataTypeController::MODEL_LOADED) { | |
| 234 TRACE_EVENT_ASYNC_BEGIN1("sync", "ModelAssociation", | |
| 235 dtc, | |
| 236 "DataType", | |
| 237 ModelTypeToString(type)); | |
| 238 | |
| 239 dtc->StartAssociating( | |
| 240 base::Bind(&ModelAssociationManager::TypeStartCallback, | |
| 241 weak_ptr_factory_.GetWeakPtr(), | |
| 242 type, base::TimeTicks::Now())); | |
| 243 } | |
| 244 } | |
| 245 } | |
| 246 | |
| 247 void ModelAssociationManager::ResetForNextAssociation() { | |
| 248 DVLOG(1) << "ModelAssociationManager: Reseting for next configuration"; | |
| 249 // |loaded_types_| and |associated_types_| are not cleared. So | |
| 250 // reconfiguration won't restart types that are already started. | |
| 251 requested_types_.Clear(); | |
| 252 failed_data_types_info_.clear(); | |
| 253 associating_types_.Clear(); | |
| 254 needs_crypto_types_.Clear(); | |
| 255 } | |
| 256 | |
| 257 void ModelAssociationManager::Stop() { | |
| 258 // Ignore callbacks from controllers. | |
| 259 weak_ptr_factory_.InvalidateWeakPtrs(); | |
| 260 | |
| 261 // Stop started data types. | |
| 262 for (DataTypeController::TypeMap::const_iterator it = controllers_->begin(); | |
| 263 it != controllers_->end(); ++it) { | |
| 264 DataTypeController* dtc = (*it).second.get(); | |
| 265 if (dtc->state() != DataTypeController::NOT_RUNNING) { | |
| 266 dtc->Stop(); | |
| 267 DVLOG(1) << "ModelAssociationManager: Stopped " << dtc->name(); | |
| 268 } | |
| 269 } | |
| 270 | |
| 271 desired_types_.Clear(); | |
| 272 loaded_types_.Clear(); | |
| 273 associated_types_.Clear(); | |
| 274 slow_types_.Clear(); | |
| 275 | |
| 276 if (state_ == CONFIGURING) { | |
| 277 if (configure_status_ == DataTypeManager::OK) | |
| 278 configure_status_ = DataTypeManager::ABORTED; | |
| 279 DVLOG(1) << "ModelAssociationManager: Calling OnModelAssociationDone"; | |
| 280 ModelAssociationDone(); | |
| 281 } | |
| 282 | |
| 283 ResetForNextAssociation(); | |
| 284 | |
| 285 state_ = IDLE; | |
| 286 } | |
| 287 | |
| 288 void ModelAssociationManager::AppendToFailedDatatypesAndLogError( | |
| 289 const syncer::SyncError& error) { | |
| 290 failed_data_types_info_[error.model_type()] = error; | |
| 291 LOG(ERROR) << "Failed to associate models for " | |
| 292 << syncer::ModelTypeToString(error.model_type()); | |
| 293 UMA_HISTOGRAM_ENUMERATION("Sync.ConfigureFailed", | |
| 294 ModelTypeToHistogramInt(error.model_type()), | |
| 295 syncer::MODEL_TYPE_COUNT); | |
| 296 } | |
| 297 | |
| 298 void ModelAssociationManager::ModelLoadCallback(syncer::ModelType type, | |
| 299 syncer::SyncError error) { | |
| 300 DVLOG(1) << "ModelAssociationManager: ModelLoadCallback for " | |
| 301 << syncer::ModelTypeToString(type); | |
| 302 | |
| 303 // TODO(haitaol): temporary fix for 335606. | |
| 304 if (slow_types_.Has(type)) | |
| 305 return; | |
| 306 | |
| 307 // This happens when slow loading type is disabled by new configuration. | |
| 308 if (!desired_types_.Has(type)) | |
| 309 return; | |
| 310 | |
| 311 DCHECK(!loaded_types_.Has(type)); | |
| 312 if (error.IsSet()) { | |
| 313 syncer::SyncMergeResult local_merge_result(type); | |
| 314 local_merge_result.set_error(error); | |
| 315 TypeStartCallback(type, | |
| 316 base::TimeTicks::Now(), | |
| 317 DataTypeController::ASSOCIATION_FAILED, | |
| 318 local_merge_result, | |
| 319 syncer::SyncMergeResult(type)); | |
| 320 return; | |
| 321 } | |
| 322 | |
| 323 loaded_types_.Put(type); | |
| 324 if (associating_types_.Has(type) || slow_types_.Has(type)) { | |
| 325 DataTypeController* dtc = controllers_->find(type)->second.get(); | |
| 326 dtc->StartAssociating( | |
| 327 base::Bind(&ModelAssociationManager::TypeStartCallback, | |
| 328 weak_ptr_factory_.GetWeakPtr(), | |
| 329 type, base::TimeTicks::Now())); | |
| 330 } | |
| 331 } | |
| 332 | |
| 333 void ModelAssociationManager::TypeStartCallback( | |
| 334 syncer::ModelType type, | |
| 335 base::TimeTicks type_start_time, | |
| 336 DataTypeController::StartResult start_result, | |
| 337 const syncer::SyncMergeResult& local_merge_result, | |
| 338 const syncer::SyncMergeResult& syncer_merge_result) { | |
| 339 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 340 | |
| 341 // TODO(haitaol): temporary fix for 335606. | |
| 342 if (slow_types_.Has(type)) | |
| 343 return; | |
| 344 | |
| 345 // This happens when slow associating type is disabled by new configuration. | |
| 346 if (!desired_types_.Has(type)) | |
| 347 return; | |
| 348 | |
| 349 slow_types_.Remove(type); | |
| 350 | |
| 351 DCHECK(!associated_types_.Has(type)); | |
| 352 if (DataTypeController::IsSuccessfulResult(start_result)) { | |
| 353 associated_types_.Put(type); | |
| 354 } else if (state_ == IDLE) { | |
| 355 // For type that failed in IDLE mode, simply stop the controller. Next | |
| 356 // configuration will try to restart from scratch if the type is still | |
| 357 // enabled. | |
| 358 DataTypeController* dtc = controllers_->find(type)->second.get(); | |
| 359 if (dtc->state() != DataTypeController::NOT_RUNNING) | |
| 360 dtc->Stop(); | |
| 361 loaded_types_.Remove(type); | |
| 362 } else { | |
| 363 // Record error in CONFIGURING or INITIALIZED_TO_CONFIGURE mode. The error | |
| 364 // will be reported when data types association finishes. | |
| 365 if (start_result == DataTypeController::NEEDS_CRYPTO) { | |
| 366 DVLOG(1) << "ModelAssociationManager: Encountered an undecryptable type"; | |
| 367 needs_crypto_types_.Put(type); | |
| 368 } else { | |
| 369 DVLOG(1) << "ModelAssociationManager: Encountered a failed type"; | |
| 370 AppendToFailedDatatypesAndLogError(local_merge_result.error()); | |
| 371 } | |
| 372 } | |
| 373 | |
| 374 if (state_ != CONFIGURING) | |
| 375 return; | |
| 376 | |
| 377 TRACE_EVENT_ASYNC_END1("sync", "ModelAssociation", | |
| 378 controllers_->find(type)->second.get(), | |
| 379 "DataType", | |
| 380 ModelTypeToString(type)); | |
| 381 | |
| 382 // Track the merge results if we succeeded or an association failure | |
| 383 // occurred. | |
| 384 if ((DataTypeController::IsSuccessfulResult(start_result) || | |
| 385 start_result == DataTypeController::ASSOCIATION_FAILED) && | |
| 386 syncer::ProtocolTypes().Has(type)) { | |
| 387 base::TimeDelta association_wait_time = | |
| 388 std::max(base::TimeDelta(), type_start_time - association_start_time_); | |
| 389 base::TimeDelta association_time = | |
| 390 base::TimeTicks::Now() - type_start_time;; | |
| 391 syncer::DataTypeAssociationStats stats = | |
| 392 BuildAssociationStatsFromMergeResults(local_merge_result, | |
| 393 syncer_merge_result, | |
| 394 association_wait_time, | |
| 395 association_time); | |
| 396 result_processor_->OnSingleDataTypeAssociationDone(type, stats); | |
| 397 } | |
| 398 | |
| 399 // Update configuration result. | |
| 400 if (configure_status_ == DataTypeManager::OK && | |
| 401 start_result == DataTypeController::ASSOCIATION_FAILED) { | |
| 402 configure_status_ = DataTypeManager::PARTIAL_SUCCESS; | |
| 403 } | |
| 404 if (start_result == DataTypeController::UNRECOVERABLE_ERROR) | |
| 405 configure_status_ = DataTypeManager::UNRECOVERABLE_ERROR; | |
| 406 | |
| 407 associating_types_.Remove(type); | |
| 408 | |
| 409 if (associating_types_.Empty()) | |
| 410 ModelAssociationDone(); | |
| 411 } | |
| 412 | |
| 413 void ModelAssociationManager::ModelAssociationDone() { | |
| 414 CHECK_EQ(CONFIGURING, state_); | |
| 415 | |
| 416 timer_.Stop(); | |
| 417 | |
| 418 slow_types_.PutAll(associating_types_); | |
| 419 | |
| 420 // TODO(haitaol): temporary fix for 335606. | |
| 421 for (syncer::ModelTypeSet::Iterator it = associating_types_.First(); | |
| 422 it.Good(); it.Inc()) { | |
| 423 AppendToFailedDatatypesAndLogError( | |
| 424 syncer::SyncError(FROM_HERE, syncer::SyncError::DATATYPE_ERROR, | |
| 425 "Association timed out.", it.Get())); | |
| 426 } | |
| 427 | |
| 428 // Stop controllers of failed types. | |
| 429 StopDisabledTypes(); | |
| 430 | |
| 431 if (configure_status_ == DataTypeManager::OK && | |
| 432 (!associating_types_.Empty() || !failed_data_types_info_.empty() || | |
| 433 !needs_crypto_types_.Empty())) { | |
| 434 // We have not configured all types that we have been asked to configure. | |
| 435 // Either we have failed types or types that have not completed loading | |
| 436 // yet. | |
| 437 DVLOG(1) << "ModelAssociationManager: setting partial success"; | |
| 438 configure_status_ = DataTypeManager::PARTIAL_SUCCESS; | |
| 439 } | |
| 440 | |
| 441 DataTypeManager::ConfigureResult result(configure_status_, | |
| 442 requested_types_, | |
| 443 failed_data_types_info_, | |
| 444 associating_types_, | |
| 445 needs_crypto_types_); | |
| 446 | |
| 447 // Reset state before notifying |result_processor_| because that might | |
| 448 // trigger a new round of configuration. | |
| 449 ResetForNextAssociation(); | |
| 450 state_ = IDLE; | |
| 451 | |
| 452 result_processor_->OnModelAssociationDone(result); | |
| 453 } | |
| 454 | |
| 455 base::OneShotTimer<ModelAssociationManager>* | |
| 456 ModelAssociationManager::GetTimerForTesting() { | |
| 457 return &timer_; | |
| 458 } | |
| 459 | |
| 460 } // namespace browser_sync | |
| OLD | NEW |