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