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