profile_sync_service.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
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/profile_sync_service.h" 6 7#include <cstddef> 8#include <map> 9#include <set> 10#include <utility> 11 12#include "base/basictypes.h" 13#include "base/bind.h" 14#include "base/callback.h" 15#include "base/command_line.h" 16#include "base/compiler_specific.h" 17#include "base/logging.h" 18#include "base/memory/ref_counted.h" 19#include "base/message_loop.h" 20#include "base/metrics/histogram.h" 21#include "base/string16.h" 22#include "base/stringprintf.h" 23#include "base/threading/thread_restrictions.h" 24#include "build/build_config.h" 25#include "chrome/browser/about_flags.h" 26#include "chrome/browser/browser_process.h" 27#include "chrome/browser/defaults.h" 28#include "chrome/browser/extensions/extension_service.h" 29#include "chrome/browser/extensions/extension_system.h" 30#include "chrome/browser/net/chrome_cookie_notification_details.h" 31#include "chrome/browser/prefs/pref_service.h" 32#include "chrome/browser/profiles/profile.h" 33#include "chrome/browser/signin/signin_manager.h" 34#include "chrome/browser/signin/signin_manager_factory.h" 35#include "chrome/browser/signin/token_service.h" 36#include "chrome/browser/signin/token_service_factory.h" 37#include "chrome/browser/sync/backend_migrator.h" 38#include "chrome/browser/sync/glue/change_processor.h" 39#include "chrome/browser/sync/glue/chrome_encryptor.h" 40#include "chrome/browser/sync/glue/chrome_report_unrecoverable_error.h" 41#include "chrome/browser/sync/glue/data_type_controller.h" 42#include "chrome/browser/sync/glue/session_data_type_controller.h" 43#include "chrome/browser/sync/glue/session_model_associator.h" 44#include "chrome/browser/sync/glue/typed_url_data_type_controller.h" 45#include "chrome/browser/sync/profile_sync_components_factory_impl.h" 46#include "chrome/browser/sync/sync_global_error.h" 47#include "chrome/browser/sync/user_selectable_sync_type.h" 48#include "chrome/browser/ui/browser.h" 49#include "chrome/browser/ui/browser_list.h" 50#include "chrome/browser/ui/browser_window.h" 51#include "chrome/browser/ui/global_error/global_error_service.h" 52#include "chrome/browser/ui/global_error/global_error_service_factory.h" 53#include "chrome/common/chrome_notification_types.h" 54#include "chrome/common/chrome_switches.h" 55#include "chrome/common/chrome_version_info.h" 56#include "chrome/common/pref_names.h" 57#include "chrome/common/time_format.h" 58#include "chrome/common/url_constants.h" 59#include "content/public/browser/notification_details.h" 60#include "content/public/browser/notification_service.h" 61#include "content/public/browser/notification_source.h" 62#include "google_apis/gaia/gaia_constants.h" 63#include "grit/generated_resources.h" 64#include "net/cookies/cookie_monster.h" 65#include "sync/api/sync_error.h" 66#include "sync/internal_api/public/configure_reason.h" 67#include "sync/internal_api/public/sync_encryption_handler.h" 68#include "sync/internal_api/public/util/experiments.h" 69#include "sync/internal_api/public/util/sync_string_conversions.h" 70#include "sync/js/js_arg_list.h" 71#include "sync/js/js_event_details.h" 72#include "sync/notifier/invalidator_registrar.h" 73#include "sync/util/cryptographer.h" 74#include "ui/base/l10n/l10n_util.h" 75 76using browser_sync::ChangeProcessor; 77using browser_sync::DataTypeController; 78using browser_sync::DataTypeManager; 79using browser_sync::SyncBackendHost; 80using syncer::ModelType; 81using syncer::ModelTypeSet; 82using syncer::JsBackend; 83using syncer::JsController; 84using syncer::JsEventDetails; 85using syncer::JsEventHandler; 86using syncer::ModelSafeRoutingInfo; 87using syncer::SyncCredentials; 88using syncer::SyncProtocolError; 89using syncer::WeakHandle; 90 91typedef GoogleServiceAuthError AuthError; 92 93const char* ProfileSyncService::kSyncServerUrl = 94 "https://clients4.google.com/chrome-sync"; 95 96const char* ProfileSyncService::kDevServerUrl = 97 "https://clients4.google.com/chrome-sync/dev"; 98 99static const int kSyncClearDataTimeoutInSeconds = 60; // 1 minute. 100 101static const char* kRelevantTokenServices[] = { 102 GaiaConstants::kSyncService 103}; 104static const int kRelevantTokenServicesCount = 105 arraysize(kRelevantTokenServices); 106 107static const char* kSyncUnrecoverableErrorHistogram = 108 "Sync.UnrecoverableErrors"; 109 110// Helper to check if the given token service is relevant for sync. 111static bool IsTokenServiceRelevant(const std::string& service) { 112 for (int i = 0; i < kRelevantTokenServicesCount; ++i) { 113 if (service == kRelevantTokenServices[i]) 114 return true; 115 } 116 return false; 117} 118 119bool ShouldShowActionOnUI( 120 const syncer::SyncProtocolError& error) { 121 return (error.action != syncer::UNKNOWN_ACTION && 122 error.action != syncer::DISABLE_SYNC_ON_CLIENT); 123} 124 125ProfileSyncService::ProfileSyncService(ProfileSyncComponentsFactory* factory, 126 Profile* profile, 127 SigninManager* signin_manager, 128 StartBehavior start_behavior) 129 : last_auth_error_(AuthError::None()), 130 passphrase_required_reason_(syncer::REASON_PASSPHRASE_NOT_REQUIRED), 131 factory_(factory), 132 profile_(profile), 133 // |profile| may be NULL in unit tests. 134 sync_prefs_(profile_ ? profile_->GetPrefs() : NULL), 135 invalidator_storage_(profile_ ? profile_->GetPrefs(): NULL), 136 sync_service_url_(kDevServerUrl), 137 is_first_time_sync_configure_(false), 138 backend_initialized_(false), 139 is_auth_in_progress_(false), 140 signin_(signin_manager), 141 unrecoverable_error_reason_(ERROR_REASON_UNSET), 142 weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), 143 expect_sync_configuration_aborted_(false), 144 encrypted_types_(syncer::SyncEncryptionHandler::SensitiveTypes()), 145 encrypt_everything_(false), 146 encryption_pending_(false), 147 auto_start_enabled_(start_behavior == AUTO_START), 148 failed_datatypes_handler_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), 149 configure_status_(DataTypeManager::UNKNOWN), 150 setup_in_progress_(false), 151 invalidator_state_(syncer::DEFAULT_INVALIDATION_ERROR) { 152#if defined(OS_ANDROID) 153 chrome::VersionInfo version_info; 154 if (version_info.IsOfficialBuild()) { 155 sync_service_url_ = GURL(kSyncServerUrl); 156 } 157#else 158 // By default, dev, canary, and unbranded Chromium users will go to the 159 // development servers. Development servers have more features than standard 160 // sync servers. Users with officially-branded Chrome stable and beta builds 161 // will go to the standard sync servers. 162 // 163 // GetChannel hits the registry on Windows. See http://crbug.com/70380. 164 base::ThreadRestrictions::ScopedAllowIO allow_io; 165 chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel(); 166 if (channel == chrome::VersionInfo::CHANNEL_STABLE || 167 channel == chrome::VersionInfo::CHANNEL_BETA) { 168 sync_service_url_ = GURL(kSyncServerUrl); 169 } 170#endif 171} 172 173ProfileSyncService::~ProfileSyncService() { 174 sync_prefs_.RemoveSyncPrefObserver(this); 175 // Shutdown() should have been called before destruction. 176 CHECK(!backend_initialized_); 177} 178 179bool ProfileSyncService::IsSyncEnabledAndLoggedIn() { 180 // Exit if sync is disabled. 181 if (IsManaged() || sync_prefs_.IsStartSuppressed()) 182 return false; 183 184 // Sync is logged in if there is a non-empty authenticated username. 185 return !signin_->GetAuthenticatedUsername().empty(); 186} 187 188bool ProfileSyncService::IsSyncTokenAvailable() { 189 TokenService* token_service = TokenServiceFactory::GetForProfile(profile_); 190 if (!token_service) 191 return false; 192 return token_service->HasTokenForService(GaiaConstants::kSyncService); 193} 194 195void ProfileSyncService::Initialize() { 196 DCHECK(!invalidator_registrar_.get()); 197 invalidator_registrar_.reset(new syncer::InvalidatorRegistrar()); 198 199 InitSettings(); 200 201 // We clear this here (vs Shutdown) because we want to remember that an error 202 // happened on shutdown so we can display details (message, location) about it 203 // in about:sync. 204 ClearStaleErrors(); 205 206 sync_prefs_.AddSyncPrefObserver(this); 207 208 // For now, the only thing we can do through policy is to turn sync off. 209 if (IsManaged()) { 210 DisableForUser(); 211 return; 212 } 213 214 RegisterAuthNotifications(); 215 216 if (!HasSyncSetupCompleted() || signin_->GetAuthenticatedUsername().empty()) { 217 // Clean up in case of previous crash / setup abort / signout. 218 DisableForUser(); 219 } 220 221 TrySyncDatatypePrefRecovery(); 222 223 TryStart(); 224} 225 226void ProfileSyncService::TrySyncDatatypePrefRecovery() { 227 DCHECK(!sync_initialized()); 228 if (!HasSyncSetupCompleted()) 229 return; 230 231 // There was a bug where OnUserChoseDatatypes was not properly called on 232 // configuration (see crbug.com/154940). We detect this by checking whether 233 // kSyncKeepEverythingSynced has a default value. If so, and sync setup has 234 // completed, it means sync was not properly configured, so we manually 235 // set kSyncKeepEverythingSynced. 236 PrefService* const pref_service = profile_->GetPrefs(); 237 if (!pref_service) 238 return; 239 if (sync_prefs_.HasKeepEverythingSynced()) 240 return; 241 const syncer::ModelTypeSet registered_types = GetRegisteredDataTypes(); 242 if (sync_prefs_.GetPreferredDataTypes(registered_types).Size() > 1) 243 return; 244 245 const PrefService::Preference* keep_everything_synced = 246 pref_service->FindPreference(prefs::kSyncKeepEverythingSynced); 247 // This will be false if the preference was properly set or if it's controlled 248 // by policy. 249 if (!keep_everything_synced->IsDefaultValue()) 250 return; 251 252 // kSyncKeepEverythingSynced was not properly set. Set it and the preferred 253 // types now, before we configure. 254 UMA_HISTOGRAM_COUNTS("Sync.DatatypePrefRecovery", 1); 255 sync_prefs_.SetKeepEverythingSynced(true); 256 sync_prefs_.SetPreferredDataTypes(registered_types, 257 registered_types); 258} 259 260void ProfileSyncService::TryStart() { 261 if (!IsSyncEnabledAndLoggedIn()) 262 return; 263 TokenService* token_service = TokenServiceFactory::GetForProfile(profile_); 264 if (!token_service) 265 return; 266 // Don't start the backend if the token service hasn't finished loading tokens 267 // yet (if the backend is started before the sync token has been loaded, 268 // GetCredentials() will return bogus credentials). On auto_start platforms 269 // (like ChromeOS) we don't start sync until tokens are loaded, because the 270 // user can be "signed in" on those platforms long before the tokens get 271 // loaded, and we don't want to generate spurious auth errors. 272 if (IsSyncTokenAvailable() || 273 (!auto_start_enabled_ && token_service->TokensLoadedFromDB())) { 274 if (HasSyncSetupCompleted() || auto_start_enabled_) { 275 // If sync setup has completed we always start the backend. 276 // If autostart is enabled, but we haven't completed sync setup, we try to 277 // start sync anyway, since it's possible we crashed/shutdown after 278 // logging in but before the backend finished initializing the last time. 279 // Note that if we haven't finished setting up sync, backend bring up will 280 // be done by the wizard. 281 StartUp(); 282 } 283 } 284} 285 286void ProfileSyncService::StartSyncingWithServer() { 287 if (backend_.get()) 288 backend_->StartSyncingWithServer(); 289} 290 291void ProfileSyncService::RegisterAuthNotifications() { 292 TokenService* token_service = TokenServiceFactory::GetForProfile(profile_); 293 registrar_.Add(this, 294 chrome::NOTIFICATION_TOKEN_AVAILABLE, 295 content::Source<TokenService>(token_service)); 296 registrar_.Add(this, 297 chrome::NOTIFICATION_TOKEN_LOADING_FINISHED, 298 content::Source<TokenService>(token_service)); 299 registrar_.Add(this, 300 chrome::NOTIFICATION_TOKEN_REQUEST_FAILED, 301 content::Source<TokenService>(token_service)); 302 registrar_.Add(this, 303 chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL, 304 content::Source<Profile>(profile_)); 305} 306 307void ProfileSyncService::RegisterDataTypeController( 308 DataTypeController* data_type_controller) { 309 DCHECK_EQ(data_type_controllers_.count(data_type_controller->type()), 0U); 310 data_type_controllers_[data_type_controller->type()] = 311 data_type_controller; 312} 313 314browser_sync::SessionModelAssociator* 315 ProfileSyncService::GetSessionModelAssociator() { 316 if (data_type_controllers_.find(syncer::SESSIONS) == 317 data_type_controllers_.end() || 318 data_type_controllers_.find(syncer::SESSIONS)->second->state() != 319 DataTypeController::RUNNING) { 320 return NULL; 321 } 322 return static_cast<browser_sync::SessionDataTypeController*>( 323 data_type_controllers_.find( 324 syncer::SESSIONS)->second.get())->GetModelAssociator(); 325} 326 327void ProfileSyncService::GetDataTypeControllerStates( 328 browser_sync::DataTypeController::StateMap* state_map) const { 329 for (browser_sync::DataTypeController::TypeMap::const_iterator iter = 330 data_type_controllers_.begin(); iter != data_type_controllers_.end(); 331 ++iter) 332 (*state_map)[iter->first] = iter->second.get()->state(); 333} 334 335void ProfileSyncService::InitSettings() { 336 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); 337 338 // Override the sync server URL from the command-line, if sync server 339 // command-line argument exists. 340 if (command_line.HasSwitch(switches::kSyncServiceURL)) { 341 std::string value(command_line.GetSwitchValueASCII( 342 switches::kSyncServiceURL)); 343 if (!value.empty()) { 344 GURL custom_sync_url(value); 345 if (custom_sync_url.is_valid()) { 346 sync_service_url_ = custom_sync_url; 347 } else { 348 LOG(WARNING) << "The following sync URL specified at the command-line " 349 << "is invalid: " << value; 350 } 351 } 352 } 353} 354 355SyncCredentials ProfileSyncService::GetCredentials() { 356 SyncCredentials credentials; 357 credentials.email = signin_->GetAuthenticatedUsername(); 358 DCHECK(!credentials.email.empty()); 359 TokenService* service = TokenServiceFactory::GetForProfile(profile_); 360 if (service->HasTokenForService(GaiaConstants::kSyncService)) { 361 credentials.sync_token = service->GetTokenForService( 362 GaiaConstants::kSyncService); 363 UMA_HISTOGRAM_BOOLEAN("Sync.CredentialsLost", false); 364 } else { 365 // We've lost our sync credentials (crbug.com/121755), so just make up some 366 // invalid credentials so the backend will generate an auth error. 367 UMA_HISTOGRAM_BOOLEAN("Sync.CredentialsLost", true); 368 credentials.sync_token = "credentials_lost"; 369 } 370 return credentials; 371} 372 373void ProfileSyncService::InitializeBackend(bool delete_stale_data) { 374 if (!backend_.get()) { 375 NOTREACHED(); 376 return; 377 } 378 379 SyncCredentials credentials = GetCredentials(); 380 381 scoped_refptr<net::URLRequestContextGetter> request_context_getter( 382 profile_->GetRequestContext()); 383 384 if (delete_stale_data) 385 ClearStaleErrors(); 386 387 backend_unrecoverable_error_handler_.reset( 388 new browser_sync::BackendUnrecoverableErrorHandler( 389 MakeWeakHandle(weak_factory_.GetWeakPtr()))); 390 391 backend_->Initialize( 392 this, 393 MakeWeakHandle(sync_js_controller_.AsWeakPtr()), 394 sync_service_url_, 395 credentials, 396 delete_stale_data, 397 &sync_manager_factory_, 398 backend_unrecoverable_error_handler_.get(), 399 &browser_sync::ChromeReportUnrecoverableError); 400} 401 402void ProfileSyncService::CreateBackend() { 403 backend_.reset( 404 new SyncBackendHost(profile_->GetDebugName(), 405 profile_, sync_prefs_.AsWeakPtr(), 406 invalidator_storage_.AsWeakPtr())); 407} 408 409bool ProfileSyncService::IsEncryptedDatatypeEnabled() const { 410 if (encryption_pending()) 411 return true; 412 const syncer::ModelTypeSet preferred_types = GetPreferredDataTypes(); 413 const syncer::ModelTypeSet encrypted_types = GetEncryptedDataTypes(); 414 DCHECK(encrypted_types.Has(syncer::PASSWORDS)); 415 return !Intersection(preferred_types, encrypted_types).Empty(); 416} 417 418void ProfileSyncService::OnSyncConfigureDone( 419 DataTypeManager::ConfigureResult result) { 420 if (failed_datatypes_handler_.UpdateFailedDatatypes(result.failed_data_types, 421 FailedDatatypesHandler::STARTUP)) { 422 ReconfigureDatatypeManager(); 423 } 424} 425 426void ProfileSyncService::OnSyncConfigureRetry() { 427 // In platforms with auto start we would just wait for the 428 // configure to finish. In other platforms we would throw 429 // an unrecoverable error. The reason we do this is so that 430 // the login dialog would show an error and the user would have 431 // to relogin. 432 // Also if backend has been initialized(the user is authenticated 433 // and nigori is downloaded) we would simply wait rather than going into 434 // unrecoverable error, even if the platform has auto start disabled. 435 // Note: In those scenarios the UI does not wait for the configuration 436 // to finish. 437 if (!auto_start_enabled_ && !backend_initialized_) { 438 OnInternalUnrecoverableError(FROM_HERE, 439 "Configure failed to download.", 440 true, 441 ERROR_REASON_CONFIGURATION_RETRY); 442 } 443 444 NotifyObservers(); 445} 446 447 448void ProfileSyncService::StartUp() { 449 // Don't start up multiple times. 450 if (backend_.get()) { 451 DVLOG(1) << "Skipping bringing up backend host."; 452 return; 453 } 454 455 DCHECK(IsSyncEnabledAndLoggedIn()); 456 457 last_synced_time_ = sync_prefs_.GetLastSyncedTime(); 458 start_up_time_ = base::Time::Now(); 459 460#if defined(OS_CHROMEOS) 461 std::string bootstrap_token = sync_prefs_.GetEncryptionBootstrapToken(); 462 if (bootstrap_token.empty()) { 463 sync_prefs_.SetEncryptionBootstrapToken( 464 sync_prefs_.GetSpareBootstrapToken()); 465 } 466#endif 467 CreateBackend(); 468 469 // Initialize the backend. Every time we start up a new SyncBackendHost, 470 // we'll want to start from a fresh SyncDB, so delete any old one that might 471 // be there. 472 InitializeBackend(!HasSyncSetupCompleted()); 473 474 // |backend_| may end up being NULL here in tests (in synchronous 475 // initialization mode). 476 // 477 // TODO(akalin): Fix this horribly non-intuitive behavior (see 478 // http://crbug.com/140354). 479 if (backend_.get()) { 480 backend_->UpdateRegisteredInvalidationIds( 481 invalidator_registrar_->GetAllRegisteredIds()); 482 } 483 484 if (!sync_global_error_.get()) { 485#if !defined(OS_ANDROID) 486 sync_global_error_.reset(new SyncGlobalError(this, signin())); 487#endif 488 GlobalErrorServiceFactory::GetForProfile(profile_)->AddGlobalError( 489 sync_global_error_.get()); 490 AddObserver(sync_global_error_.get()); 491 } 492} 493 494void ProfileSyncService::RegisterInvalidationHandler( 495 syncer::InvalidationHandler* handler) { 496 invalidator_registrar_->RegisterHandler(handler); 497} 498 499void ProfileSyncService::UpdateRegisteredInvalidationIds( 500 syncer::InvalidationHandler* handler, 501 const syncer::ObjectIdSet& ids) { 502 invalidator_registrar_->UpdateRegisteredIds(handler, ids); 503 504 // If |backend_| is NULL, its registered IDs will be updated when 505 // it's created and initialized. 506 if (backend_.get()) { 507 backend_->UpdateRegisteredInvalidationIds( 508 invalidator_registrar_->GetAllRegisteredIds()); 509 } 510} 511 512void ProfileSyncService::UnregisterInvalidationHandler( 513 syncer::InvalidationHandler* handler) { 514 invalidator_registrar_->UnregisterHandler(handler); 515} 516 517syncer::InvalidatorState ProfileSyncService::GetInvalidatorState() const { 518 return invalidator_registrar_->GetInvalidatorState(); 519} 520 521void ProfileSyncService::EmitInvalidationForTest( 522 const invalidation::ObjectId& id, 523 const std::string& payload) { 524 syncer::ObjectIdSet notify_ids; 525 notify_ids.insert(id); 526 527 const syncer::ObjectIdInvalidationMap& invalidation_map = 528 ObjectIdSetToInvalidationMap(notify_ids, payload); 529 OnIncomingInvalidation(invalidation_map, syncer::REMOTE_INVALIDATION); 530} 531 532void ProfileSyncService::Shutdown() { 533 DCHECK(invalidator_registrar_.get()); 534 // TODO(akalin): Remove this once http://crbug.com/153827 is fixed. 535 ExtensionService* const extension_service = 536 extensions::ExtensionSystem::Get(profile_)->extension_service(); 537 // |extension_service| may be NULL if it was never initialized 538 // (e.g., extension sync wasn't enabled in tests). 539 if (extension_service) 540 extension_service->OnProfileSyncServiceShutdown(); 541 542 // Reset |invalidator_registrar_| first so that ShutdownImpl cannot 543 // use it. 544 invalidator_registrar_.reset(); 545 546 ShutdownImpl(false); 547} 548 549void ProfileSyncService::ShutdownImpl(bool sync_disabled) { 550 // First, we spin down the backend and wait for it to stop syncing completely 551 // before we Stop the data type manager. This is to avoid a late sync cycle 552 // applying changes to the sync db that wouldn't get applied via 553 // ChangeProcessors, leading to back-from-the-dead bugs. 554 base::Time shutdown_start_time = base::Time::Now(); 555 if (backend_.get()) { 556 backend_->StopSyncingForShutdown(); 557 } 558 559 // Stop all data type controllers, if needed. Note that until Stop 560 // completes, it is possible in theory to have a ChangeProcessor apply a 561 // change from a native model. In that case, it will get applied to the sync 562 // database (which doesn't get destroyed until we destroy the backend below) 563 // as an unsynced change. That will be persisted, and committed on restart. 564 if (data_type_manager_.get()) { 565 if (data_type_manager_->state() != DataTypeManager::STOPPED) { 566 // When aborting as part of shutdown, we should expect an aborted sync 567 // configure result, else we'll dcheck when we try to read the sync error. 568 expect_sync_configuration_aborted_ = true; 569 data_type_manager_->Stop(); 570 } 571 data_type_manager_.reset(); 572 } 573 574 // Shutdown the migrator before the backend to ensure it doesn't pull a null 575 // snapshot. 576 migrator_.reset(); 577 sync_js_controller_.AttachJsBackend(WeakHandle<syncer::JsBackend>()); 578 579 // Move aside the backend so nobody else tries to use it while we are 580 // shutting it down. 581 scoped_ptr<SyncBackendHost> doomed_backend(backend_.release()); 582 if (doomed_backend.get()) { 583 doomed_backend->Shutdown(sync_disabled); 584 585 doomed_backend.reset(); 586 } 587 base::TimeDelta shutdown_time = base::Time::Now() - shutdown_start_time; 588 UMA_HISTOGRAM_TIMES("Sync.Shutdown.BackendDestroyedTime", shutdown_time); 589 590 weak_factory_.InvalidateWeakPtrs(); 591 592 // Clear various flags. 593 expect_sync_configuration_aborted_ = false; 594 is_auth_in_progress_ = false; 595 backend_initialized_ = false; 596 // NULL if we're called from Shutdown(). 597 if (invalidator_registrar_.get()) 598 UpdateInvalidatorRegistrarState(); 599 cached_passphrase_.clear(); 600 encryption_pending_ = false; 601 encrypt_everything_ = false; 602 encrypted_types_ = syncer::SyncEncryptionHandler::SensitiveTypes(); 603 passphrase_required_reason_ = syncer::REASON_PASSPHRASE_NOT_REQUIRED; 604 last_auth_error_ = AuthError::None(); 605 606 if (sync_global_error_.get()) { 607 GlobalErrorServiceFactory::GetForProfile(profile_)->RemoveGlobalError( 608 sync_global_error_.get()); 609 RemoveObserver(sync_global_error_.get()); 610 sync_global_error_.reset(NULL); 611 } 612} 613 614void ProfileSyncService::DisableForUser() { 615 // Clear prefs (including SyncSetupHasCompleted) before shutting down so 616 // PSS clients don't think we're set up while we're shutting down. 617 sync_prefs_.ClearPreferences(); 618 invalidator_storage_.Clear(); 619 ClearUnrecoverableError(); 620 ShutdownImpl(true); 621 622 // TODO(atwilson): Don't call SignOut() on *any* platform - move this into 623 // the UI layer if needed (sync activity should never result in the user 624 // being logged out of all chrome services). 625 if (!auto_start_enabled_ && !signin_->GetAuthenticatedUsername().empty()) 626 signin_->SignOut(); 627 628 NotifyObservers(); 629} 630 631bool ProfileSyncService::HasSyncSetupCompleted() const { 632 return sync_prefs_.HasSyncSetupCompleted(); 633} 634 635void ProfileSyncService::SetSyncSetupCompleted() { 636 sync_prefs_.SetSyncSetupCompleted(); 637} 638 639void ProfileSyncService::UpdateLastSyncedTime() { 640 last_synced_time_ = base::Time::Now(); 641 sync_prefs_.SetLastSyncedTime(last_synced_time_); 642} 643 644void ProfileSyncService::NotifyObservers() { 645 FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged()); 646 // TODO(akalin): Make an Observer subclass that listens and does the 647 // event routing. 648 sync_js_controller_.HandleJsEvent( 649 "onServiceStateChanged", JsEventDetails()); 650} 651 652void ProfileSyncService::ClearStaleErrors() { 653 ClearUnrecoverableError(); 654 last_actionable_error_ = SyncProtocolError(); 655} 656 657void ProfileSyncService::ClearUnrecoverableError() { 658 unrecoverable_error_reason_ = ERROR_REASON_UNSET; 659 unrecoverable_error_message_.clear(); 660 unrecoverable_error_location_ = tracked_objects::Location(); 661} 662 663// static 664std::string ProfileSyncService::GetExperimentNameForDataType( 665 syncer::ModelType data_type) { 666 switch (data_type) { 667 case syncer::SESSIONS: 668 return "sync-tabs"; 669 default: 670 break; 671 } 672 NOTREACHED(); 673 return ""; 674} 675 676void ProfileSyncService::RegisterNewDataType(syncer::ModelType data_type) { 677 if (data_type_controllers_.count(data_type) > 0) 678 return; 679 NOTREACHED(); 680} 681 682// An invariant has been violated. Transition to an error state where we try 683// to do as little work as possible, to avoid further corruption or crashes. 684void ProfileSyncService::OnUnrecoverableError( 685 const tracked_objects::Location& from_here, 686 const std::string& message) { 687 // Unrecoverable errors that arrive via the syncer::UnrecoverableErrorHandler 688 // interface are assumed to originate within the syncer. 689 unrecoverable_error_reason_ = ERROR_REASON_SYNCER; 690 OnUnrecoverableErrorImpl(from_here, message, true); 691} 692 693void ProfileSyncService::OnUnrecoverableErrorImpl( 694 const tracked_objects::Location& from_here, 695 const std::string& message, 696 bool delete_sync_database) { 697 DCHECK(HasUnrecoverableError()); 698 unrecoverable_error_message_ = message; 699 unrecoverable_error_location_ = from_here; 700 701 UMA_HISTOGRAM_ENUMERATION(kSyncUnrecoverableErrorHistogram, 702 unrecoverable_error_reason_, 703 ERROR_REASON_LIMIT); 704 NotifyObservers(); 705 std::string location; 706 from_here.Write(true, true, &location); 707 LOG(ERROR) 708 << "Unrecoverable error detected at " << location 709 << " -- ProfileSyncService unusable: " << message; 710 711 // Shut all data types down. 712 MessageLoop::current()->PostTask(FROM_HERE, 713 base::Bind(&ProfileSyncService::ShutdownImpl, weak_factory_.GetWeakPtr(), 714 delete_sync_database)); 715} 716 717void ProfileSyncService::DisableBrokenDatatype( 718 syncer::ModelType type, 719 const tracked_objects::Location& from_here, 720 std::string message) { 721 // First deactivate the type so that no further server changes are 722 // passed onto the change processor. 723 DeactivateDataType(type); 724 725 syncer::SyncError error(from_here, message, type); 726 727 std::list<syncer::SyncError> errors; 728 errors.push_back(error); 729 730 // Update this before posting a task. So if a configure happens before 731 // the task that we are going to post, this type would still be disabled. 732 failed_datatypes_handler_.UpdateFailedDatatypes(errors, 733 FailedDatatypesHandler::RUNTIME); 734 735 MessageLoop::current()->PostTask(FROM_HERE, 736 base::Bind(&ProfileSyncService::ReconfigureDatatypeManager, 737 weak_factory_.GetWeakPtr())); 738} 739 740void ProfileSyncService::OnInvalidatorStateChange( 741 syncer::InvalidatorState state) { 742 invalidator_state_ = state; 743 UpdateInvalidatorRegistrarState(); 744} 745 746void ProfileSyncService::OnIncomingInvalidation( 747 const syncer::ObjectIdInvalidationMap& invalidation_map, 748 syncer::IncomingInvalidationSource source) { 749 invalidator_registrar_->DispatchInvalidationsToHandlers(invalidation_map, 750 source); 751} 752 753void ProfileSyncService::OnBackendInitialized( 754 const syncer::WeakHandle<syncer::JsBackend>& js_backend, 755 const syncer::WeakHandle<syncer::DataTypeDebugInfoListener>& 756 debug_info_listener, 757 bool success) { 758 is_first_time_sync_configure_ = !HasSyncSetupCompleted(); 759 760 if (is_first_time_sync_configure_) { 761 UMA_HISTOGRAM_BOOLEAN("Sync.BackendInitializeFirstTimeSuccess", success); 762 } else { 763 UMA_HISTOGRAM_BOOLEAN("Sync.BackendInitializeRestoreSuccess", success); 764 } 765 766 if (!start_up_time_.is_null()) { 767 base::Time on_backend_initialized_time = base::Time::Now(); 768 base::TimeDelta delta = on_backend_initialized_time - start_up_time_; 769 if (is_first_time_sync_configure_) { 770 UMA_HISTOGRAM_LONG_TIMES("Sync.BackendInitializeFirstTime", delta); 771 } else { 772 UMA_HISTOGRAM_LONG_TIMES("Sync.BackendInitializeRestoreTime", delta); 773 } 774 start_up_time_ = base::Time(); 775 } 776 777 if (!success) { 778 // Something went unexpectedly wrong. Play it safe: stop syncing at once 779 // and surface error UI to alert the user sync has stopped. 780 // Keep the directory around for now so that on restart we will retry 781 // again and potentially succeed in presence of transient file IO failures 782 // or permissions issues, etc. 783 // 784 // TODO(rlarocque): Consider making this UnrecoverableError less special. 785 // Unlike every other UnrecoverableError, it does not delete our sync data. 786 // This exception made sense at the time it was implemented, but our new 787 // directory corruption recovery mechanism makes it obsolete. By the time 788 // we get here, we will have already tried and failed to delete the 789 // directory. It would be no big deal if we tried to delete it again. 790 OnInternalUnrecoverableError(FROM_HERE, 791 "BackendInitialize failure", 792 false, 793 ERROR_REASON_BACKEND_INIT_FAILURE); 794 return; 795 } 796 797 backend_initialized_ = true; 798 UpdateInvalidatorRegistrarState(); 799 800 sync_js_controller_.AttachJsBackend(js_backend); 801 debug_info_listener_ = debug_info_listener; 802 803 // If we have a cached passphrase use it to decrypt/encrypt data now that the 804 // backend is initialized. We want to call this before notifying observers in 805 // case this operation affects the "passphrase required" status. 806 ConsumeCachedPassphraseIfPossible(); 807 808 // The very first time the backend initializes is effectively the first time 809 // we can say we successfully "synced". last_synced_time_ will only be null 810 // in this case, because the pref wasn't restored on StartUp. 811 if (last_synced_time_.is_null()) { 812 UpdateLastSyncedTime(); 813 } 814 NotifyObservers(); 815 816 if (auto_start_enabled_ && !FirstSetupInProgress()) { 817 // Backend is initialized but we're not in sync setup, so this must be an 818 // autostart - mark our sync setup as completed and we'll start syncing 819 // below. 820 SetSyncSetupCompleted(); 821 NotifyObservers(); 822 } 823 824 if (HasSyncSetupCompleted()) { 825 ConfigureDataTypeManager(); 826 } else { 827 DCHECK(FirstSetupInProgress()); 828 } 829} 830 831void ProfileSyncService::OnSyncCycleCompleted() { 832 UpdateLastSyncedTime(); 833 if (GetSessionModelAssociator()) { 834 // Trigger garbage collection of old sessions now that we've downloaded 835 // any new session data. TODO(zea): Have this be a notification the session 836 // model associator listens too. Also consider somehow plumbing the current 837 // server time as last reported by CheckServerReachable, so we don't have to 838 // rely on the local clock, which may be off significantly. 839 MessageLoop::current()->PostTask(FROM_HERE, 840 base::Bind(&browser_sync::SessionModelAssociator::DeleteStaleSessions, 841 GetSessionModelAssociator()->AsWeakPtr())); 842 } 843 DVLOG(2) << "Notifying observers sync cycle completed"; 844 NotifyObservers(); 845} 846 847void ProfileSyncService::OnExperimentsChanged( 848 const syncer::Experiments& experiments) { 849 if (current_experiments.Matches(experiments)) 850 return; 851 852 // If this is a first time sync for a client, this will be called before 853 // OnBackendInitialized() to ensure the new datatypes are available at sync 854 // setup. As a result, the migrator won't exist yet. This is fine because for 855 // first time sync cases we're only concerned with making the datatype 856 // available. 857 if (migrator_.get() && 858 migrator_->state() != browser_sync::BackendMigrator::IDLE) { 859 DVLOG(1) << "Dropping OnExperimentsChanged due to migrator busy."; 860 return; 861 } 862 863 const syncer::ModelTypeSet registered_types = GetRegisteredDataTypes(); 864 syncer::ModelTypeSet to_add; 865 const syncer::ModelTypeSet to_register = 866 Difference(to_add, registered_types); 867 DVLOG(2) << "OnExperimentsChanged called with types: " 868 << syncer::ModelTypeSetToString(to_add); 869 DVLOG(2) << "Enabling types: " << syncer::ModelTypeSetToString(to_register); 870 871 for (syncer::ModelTypeSet::Iterator it = to_register.First(); 872 it.Good(); it.Inc()) { 873 // Received notice to enable experimental type. Check if the type is 874 // registered, and if not register a new datatype controller. 875 RegisterNewDataType(it.Get()); 876 // Enable the about:flags switch for the experimental type so we don't have 877 // to always perform this reconfiguration. Once we set this, the type will 878 // remain registered on restart, so we will no longer go down this code 879 // path. 880 std::string experiment_name = GetExperimentNameForDataType(it.Get()); 881 if (experiment_name.empty()) 882 continue; 883 about_flags::SetExperimentEnabled(g_browser_process->local_state(), 884 experiment_name, 885 true); 886 } 887 888 // Check if the user has "Keep Everything Synced" enabled. If so, we want 889 // to turn on all experimental types if they're not already on. Otherwise we 890 // leave them off. 891 // Note: if any types are already registered, we don't turn them on. This 892 // covers the case where we're already in the process of reconfiguring 893 // to turn an experimental type on. 894 if (sync_prefs_.HasKeepEverythingSynced()) { 895 // Mark all data types as preferred. 896 sync_prefs_.SetPreferredDataTypes(registered_types, registered_types); 897 898 // Only automatically turn on types if we have already finished set up. 899 // Otherwise, just leave the experimental types on by default. 900 if (!to_register.Empty() && HasSyncSetupCompleted() && migrator_.get()) { 901 DVLOG(1) << "Dynamically enabling new datatypes: " 902 << syncer::ModelTypeSetToString(to_register); 903 OnMigrationNeededForTypes(to_register); 904 } 905 } 906 907 // Now enable any non-datatype features. 908 if (experiments.sync_tab_favicons) { 909 DVLOG(1) << "Enabling syncing of tab favicons."; 910 about_flags::SetExperimentEnabled(g_browser_process->local_state(), 911 "sync-tab-favicons", 912 true); 913#if defined(OS_ANDROID) 914 // Android does not support about:flags and experiments, so we need to force 915 // setting the experiments as command line switches. 916 CommandLine::ForCurrentProcess()->AppendSwitch(switches::kSyncTabFavicons); 917#endif 918 } 919 920 current_experiments = experiments; 921} 922 923void ProfileSyncService::UpdateAuthErrorState(const AuthError& error) { 924 is_auth_in_progress_ = false; 925 last_auth_error_ = error; 926 927 // Fan the notification out to interested UI-thread components. 928 NotifyObservers(); 929} 930 931namespace { 932 933AuthError ConnectionStatusToAuthError( 934 syncer::ConnectionStatus status) { 935 switch (status) { 936 case syncer::CONNECTION_OK: 937 return AuthError::None(); 938 break; 939 case syncer::CONNECTION_AUTH_ERROR: 940 return AuthError(AuthError::INVALID_GAIA_CREDENTIALS); 941 break; 942 case syncer::CONNECTION_SERVER_ERROR: 943 return AuthError(AuthError::CONNECTION_FAILED); 944 break; 945 default: 946 NOTREACHED(); 947 return AuthError(AuthError::CONNECTION_FAILED); 948 } 949} 950 951} // namespace 952 953void ProfileSyncService::OnConnectionStatusChange( 954 syncer::ConnectionStatus status) { 955 UpdateAuthErrorState(ConnectionStatusToAuthError(status)); 956} 957 958void ProfileSyncService::OnStopSyncingPermanently() { 959 UpdateAuthErrorState(AuthError(AuthError::SERVICE_UNAVAILABLE)); 960 sync_prefs_.SetStartSuppressed(true); 961 DisableForUser(); 962} 963 964void ProfileSyncService::OnPassphraseRequired( 965 syncer::PassphraseRequiredReason reason, 966 const sync_pb::EncryptedData& pending_keys) { 967 DCHECK(backend_.get()); 968 DCHECK(backend_->IsNigoriEnabled()); 969 970 // TODO(lipalani) : add this check to other locations as well. 971 if (HasUnrecoverableError()) { 972 // When unrecoverable error is detected we post a task to shutdown the 973 // backend. The task might not have executed yet. 974 return; 975 } 976 977 DVLOG(1) << "Passphrase required with reason: " 978 << syncer::PassphraseRequiredReasonToString(reason); 979 passphrase_required_reason_ = reason; 980 981 // Notify observers that the passphrase status may have changed. 982 NotifyObservers(); 983} 984 985void ProfileSyncService::OnPassphraseAccepted() { 986 DVLOG(1) << "Received OnPassphraseAccepted."; 987 // If we are not using an explicit passphrase, and we have a cache of the gaia 988 // password, use it for encryption at this point. 989 DCHECK(cached_passphrase_.empty()) << 990 "Passphrase no longer required but there is still a cached passphrase"; 991 992 // Reset passphrase_required_reason_ since we know we no longer require the 993 // passphrase. We do this here rather than down in ResolvePassphraseRequired() 994 // because that can be called by OnPassphraseRequired() if no encrypted data 995 // types are enabled, and we don't want to clobber the true passphrase error. 996 passphrase_required_reason_ = syncer::REASON_PASSPHRASE_NOT_REQUIRED; 997 998 // Make sure the data types that depend on the passphrase are started at 999 // this time. 1000 const syncer::ModelTypeSet types = GetPreferredDataTypes(); 1001 1002 if (data_type_manager_.get()) { 1003 // Unblock the data type manager if necessary. 1004 data_type_manager_->Configure(types, 1005 syncer::CONFIGURE_REASON_RECONFIGURATION); 1006 } 1007 1008 NotifyObservers(); 1009} 1010 1011void ProfileSyncService::OnEncryptedTypesChanged( 1012 syncer::ModelTypeSet encrypted_types, 1013 bool encrypt_everything) { 1014 encrypted_types_ = encrypted_types; 1015 encrypt_everything_ = encrypt_everything; 1016 DVLOG(1) << "Encrypted types changed to " 1017 << syncer::ModelTypeSetToString(encrypted_types_) 1018 << " (encrypt everything is set to " 1019 << (encrypt_everything_ ? "true" : "false") << ")"; 1020 DCHECK(encrypted_types_.Has(syncer::PASSWORDS)); 1021} 1022 1023void ProfileSyncService::OnEncryptionComplete() { 1024 DVLOG(1) << "Encryption complete"; 1025 if (encryption_pending_ && encrypt_everything_) { 1026 encryption_pending_ = false; 1027 // This is to nudge the integration tests when encryption is 1028 // finished. 1029 NotifyObservers(); 1030 } 1031} 1032 1033void ProfileSyncService::OnMigrationNeededForTypes( 1034 syncer::ModelTypeSet types) { 1035 DCHECK(backend_initialized_); 1036 DCHECK(data_type_manager_.get()); 1037 1038 // Migrator must be valid, because we don't sync until it is created and this 1039 // callback originates from a sync cycle. 1040 migrator_->MigrateTypes(types); 1041} 1042 1043void ProfileSyncService::OnActionableError(const SyncProtocolError& error) { 1044 last_actionable_error_ = error; 1045 DCHECK_NE(last_actionable_error_.action, 1046 syncer::UNKNOWN_ACTION); 1047 switch (error.action) { 1048 case syncer::UPGRADE_CLIENT: 1049 case syncer::CLEAR_USER_DATA_AND_RESYNC: 1050 case syncer::ENABLE_SYNC_ON_ACCOUNT: 1051 case syncer::STOP_AND_RESTART_SYNC: 1052 // TODO(lipalani) : if setup in progress we want to display these 1053 // actions in the popup. The current experience might not be optimal for 1054 // the user. We just dismiss the dialog. 1055 if (setup_in_progress_) { 1056 OnStopSyncingPermanently(); 1057 expect_sync_configuration_aborted_ = true; 1058 } 1059 // Trigger an unrecoverable error to stop syncing. 1060 OnInternalUnrecoverableError(FROM_HERE, 1061 last_actionable_error_.error_description, 1062 true, 1063 ERROR_REASON_ACTIONABLE_ERROR); 1064 break; 1065 case syncer::DISABLE_SYNC_ON_CLIENT: 1066 OnStopSyncingPermanently(); 1067 break; 1068 default: 1069 NOTREACHED(); 1070 } 1071 NotifyObservers(); 1072} 1073 1074void ProfileSyncService::OnConfigureBlocked() { 1075 NotifyObservers(); 1076} 1077 1078void ProfileSyncService::OnConfigureDone( 1079 const browser_sync::DataTypeManager::ConfigureResult& result) { 1080 // We should have cleared our cached passphrase before we get here (in 1081 // OnBackendInitialized()). 1082 DCHECK(cached_passphrase_.empty()); 1083 1084 if (!sync_configure_start_time_.is_null()) { 1085 if (result.status == DataTypeManager::OK || 1086 result.status == DataTypeManager::PARTIAL_SUCCESS) { 1087 base::Time sync_configure_stop_time = base::Time::Now(); 1088 base::TimeDelta delta = sync_configure_stop_time - 1089 sync_configure_start_time_; 1090 if (is_first_time_sync_configure_) { 1091 UMA_HISTOGRAM_LONG_TIMES("Sync.ServiceInitialConfigureTime", delta); 1092 } else { 1093 UMA_HISTOGRAM_LONG_TIMES("Sync.ServiceSubsequentConfigureTime", 1094 delta); 1095 } 1096 } 1097 sync_configure_start_time_ = base::Time(); 1098 } 1099 1100 // Notify listeners that configuration is done. 1101 content::NotificationService::current()->Notify( 1102 chrome::NOTIFICATION_SYNC_CONFIGURE_DONE, 1103 content::Source<ProfileSyncService>(this), 1104 content::NotificationService::NoDetails()); 1105 1106 configure_status_ = result.status; 1107 DVLOG(1) << "PSS OnConfigureDone called with status: " << configure_status_; 1108 // The possible status values: 1109 // ABORT - Configuration was aborted. This is not an error, if 1110 // initiated by user. 1111 // OK - Everything succeeded. 1112 // PARTIAL_SUCCESS - Some datatypes failed to start. 1113 // Everything else is an UnrecoverableError. So treat it as such. 1114 1115 // First handle the abort case. 1116 if (configure_status_ == DataTypeManager::ABORTED && 1117 expect_sync_configuration_aborted_) { 1118 DVLOG(0) << "ProfileSyncService::Observe Sync Configure aborted"; 1119 expect_sync_configuration_aborted_ = false; 1120 return; 1121 } 1122 1123 // Handle unrecoverable error. 1124 if (configure_status_ != DataTypeManager::OK && 1125 configure_status_ != DataTypeManager::PARTIAL_SUCCESS) { 1126 // Something catastrophic had happened. We should only have one 1127 // error representing it. 1128 DCHECK_EQ(result.failed_data_types.size(), 1129 static_cast<unsigned int>(1)); 1130 syncer::SyncError error = result.failed_data_types.front(); 1131 DCHECK(error.IsSet()); 1132 std::string message = 1133 "Sync configuration failed with status " + 1134 DataTypeManager::ConfigureStatusToString(configure_status_) + 1135 " during " + syncer::ModelTypeToString(error.type()) + 1136 ": " + error.message(); 1137 LOG(ERROR) << "ProfileSyncService error: " 1138 << message; 1139 OnInternalUnrecoverableError(error.location(), 1140 message, 1141 true, 1142 ERROR_REASON_CONFIGURATION_FAILURE); 1143 return; 1144 } 1145 1146 // Now handle partial success and full success. 1147 MessageLoop::current()->PostTask(FROM_HERE, 1148 base::Bind(&ProfileSyncService::OnSyncConfigureDone, 1149 weak_factory_.GetWeakPtr(), result)); 1150 1151 // We should never get in a state where we have no encrypted datatypes 1152 // enabled, and yet we still think we require a passphrase for decryption. 1153 DCHECK(!(IsPassphraseRequiredForDecryption() && 1154 !IsEncryptedDatatypeEnabled())); 1155 1156 // This must be done before we start syncing with the server to avoid 1157 // sending unencrypted data up on a first time sync. 1158 if (encryption_pending_) 1159 backend_->EnableEncryptEverything(); 1160 NotifyObservers(); 1161 1162 if (migrator_.get() && 1163 migrator_->state() != browser_sync::BackendMigrator::IDLE) { 1164 // Migration in progress. Let the migrator know we just finished 1165 // configuring something. It will be up to the migrator to call 1166 // StartSyncingWithServer() if migration is now finished. 1167 migrator_->OnConfigureDone(result); 1168 } else { 1169 StartSyncingWithServer(); 1170 } 1171} 1172 1173void ProfileSyncService::OnConfigureRetry() { 1174 // We should have cleared our cached passphrase before we get here (in 1175 // OnBackendInitialized()). 1176 DCHECK(cached_passphrase_.empty()); 1177 1178 OnSyncConfigureRetry(); 1179} 1180 1181void ProfileSyncService::OnConfigureStart() { 1182 sync_configure_start_time_ = base::Time::Now(); 1183 content::NotificationService::current()->Notify( 1184 chrome::NOTIFICATION_SYNC_CONFIGURE_START, 1185 content::Source<ProfileSyncService>(this), 1186 content::NotificationService::NoDetails()); 1187 NotifyObservers(); 1188} 1189 1190std::string ProfileSyncService::QuerySyncStatusSummary() { 1191 if (HasUnrecoverableError()) { 1192 return "Unrecoverable error detected"; 1193 } else if (!backend_.get()) { 1194 return "Syncing not enabled"; 1195 } else if (backend_.get() && !HasSyncSetupCompleted()) { 1196 return "First time sync setup incomplete"; 1197 } else if (backend_.get() && HasSyncSetupCompleted() && 1198 data_type_manager_.get() && 1199 data_type_manager_->state() != DataTypeManager::CONFIGURED) { 1200 return "Datatypes not fully initialized"; 1201 } else if (ShouldPushChanges()) { 1202 return "Sync service initialized"; 1203 } else { 1204 return "Status unknown: Internal error?"; 1205 } 1206} 1207 1208bool ProfileSyncService::QueryDetailedSyncStatus( 1209 SyncBackendHost::Status* result) { 1210 if (backend_.get() && backend_initialized_) { 1211 *result = backend_->GetDetailedStatus(); 1212 return true; 1213 } else { 1214 SyncBackendHost::Status status; 1215 status.sync_protocol_error = last_actionable_error_; 1216 *result = status; 1217 return false; 1218 } 1219} 1220 1221const AuthError& ProfileSyncService::GetAuthError() const { 1222 return last_auth_error_; 1223} 1224 1225bool ProfileSyncService::FirstSetupInProgress() const { 1226 return !HasSyncSetupCompleted() && setup_in_progress_; 1227} 1228 1229void ProfileSyncService::SetSetupInProgress(bool setup_in_progress) { 1230 bool was_in_progress = setup_in_progress_; 1231 setup_in_progress_ = setup_in_progress; 1232 if (!setup_in_progress && was_in_progress) { 1233 if (sync_initialized()) { 1234 ReconfigureDatatypeManager(); 1235 } 1236 } 1237} 1238 1239bool ProfileSyncService::sync_initialized() const { 1240 return backend_initialized_; 1241} 1242 1243bool ProfileSyncService::waiting_for_auth() const { 1244 return is_auth_in_progress_; 1245} 1246 1247bool ProfileSyncService::HasUnrecoverableError() const { 1248 return unrecoverable_error_reason_ != ERROR_REASON_UNSET; 1249} 1250 1251bool ProfileSyncService::IsPassphraseRequired() const { 1252 return passphrase_required_reason_ != 1253 syncer::REASON_PASSPHRASE_NOT_REQUIRED; 1254} 1255 1256// TODO(zea): Rename this IsPassphraseNeededFromUI and ensure it's used 1257// appropriately (see http://crbug.com/91379). 1258bool ProfileSyncService::IsPassphraseRequiredForDecryption() const { 1259 // If there is an encrypted datatype enabled and we don't have the proper 1260 // passphrase, we must prompt the user for a passphrase. The only way for the 1261 // user to avoid entering their passphrase is to disable the encrypted types. 1262 return IsEncryptedDatatypeEnabled() && IsPassphraseRequired(); 1263} 1264 1265string16 ProfileSyncService::GetLastSyncedTimeString() const { 1266 if (last_synced_time_.is_null()) 1267 return l10n_util::GetStringUTF16(IDS_SYNC_TIME_NEVER); 1268 1269 base::TimeDelta last_synced = base::Time::Now() - last_synced_time_; 1270 1271 if (last_synced < base::TimeDelta::FromMinutes(1)) 1272 return l10n_util::GetStringUTF16(IDS_SYNC_TIME_JUST_NOW); 1273 1274 return TimeFormat::TimeElapsed(last_synced); 1275} 1276 1277void ProfileSyncService::UpdateSelectedTypesHistogram( 1278 bool sync_everything, const syncer::ModelTypeSet chosen_types) const { 1279 if (!HasSyncSetupCompleted() || 1280 sync_everything != sync_prefs_.HasKeepEverythingSynced()) { 1281 UMA_HISTOGRAM_BOOLEAN("Sync.SyncEverything", sync_everything); 1282 } 1283 1284 // Only log the data types that are shown in the sync settings ui. 1285 const syncer::ModelType model_types[] = { 1286 syncer::APPS, 1287 syncer::AUTOFILL, 1288 syncer::BOOKMARKS, 1289 syncer::EXTENSIONS, 1290 syncer::PASSWORDS, 1291 syncer::PREFERENCES, 1292 syncer::SESSIONS, 1293 syncer::THEMES, 1294 syncer::TYPED_URLS 1295 }; 1296 1297 const browser_sync::user_selectable_type::UserSelectableSyncType 1298 user_selectable_types[] = { 1299 browser_sync::user_selectable_type::APPS, 1300 browser_sync::user_selectable_type::AUTOFILL, 1301 browser_sync::user_selectable_type::BOOKMARKS, 1302 browser_sync::user_selectable_type::EXTENSIONS, 1303 browser_sync::user_selectable_type::PASSWORDS, 1304 browser_sync::user_selectable_type::PREFERENCES, 1305 browser_sync::user_selectable_type::SESSIONS, 1306 browser_sync::user_selectable_type::THEMES, 1307 browser_sync::user_selectable_type::TYPED_URLS 1308 }; 1309 1310 COMPILE_ASSERT(20 == syncer::MODEL_TYPE_COUNT, UpdateCustomConfigHistogram); 1311 COMPILE_ASSERT(arraysize(model_types) == 1312 browser_sync::user_selectable_type::SELECTABLE_DATATYPE_COUNT, 1313 UpdateCustomConfigHistogram); 1314 COMPILE_ASSERT(arraysize(model_types) == arraysize(user_selectable_types), 1315 UpdateCustomConfigHistogram); 1316 1317 if (!sync_everything) { 1318 const syncer::ModelTypeSet current_types = GetPreferredDataTypes(); 1319 for (size_t i = 0; i < arraysize(model_types); ++i) { 1320 const syncer::ModelType type = model_types[i]; 1321 if (chosen_types.Has(type) && 1322 (!HasSyncSetupCompleted() || !current_types.Has(type))) { 1323 // Selected type has changed - log it. 1324 UMA_HISTOGRAM_ENUMERATION( 1325 "Sync.CustomSync", 1326 user_selectable_types[i], 1327 browser_sync::user_selectable_type::SELECTABLE_DATATYPE_COUNT + 1); 1328 } 1329 } 1330 } 1331} 1332 1333#if defined(OS_CHROMEOS) 1334void ProfileSyncService::RefreshSpareBootstrapToken( 1335 const std::string& passphrase) { 1336 browser_sync::ChromeEncryptor encryptor; 1337 syncer::Cryptographer temp_cryptographer(&encryptor); 1338 // The first 2 params (hostname and username) doesn't have any effect here. 1339 syncer::KeyParams key_params = {"localhost", "dummy", passphrase}; 1340 1341 std::string bootstrap_token; 1342 if (!temp_cryptographer.AddKey(key_params)) { 1343 NOTREACHED() << "Failed to add key to cryptographer."; 1344 } 1345 temp_cryptographer.GetBootstrapToken(&bootstrap_token); 1346 sync_prefs_.SetSpareBootstrapToken(bootstrap_token); 1347} 1348#endif 1349 1350void ProfileSyncService::OnUserChoseDatatypes(bool sync_everything, 1351 syncer::ModelTypeSet chosen_types) { 1352 if (!backend_.get() && !HasUnrecoverableError()) { 1353 NOTREACHED(); 1354 return; 1355 } 1356 1357 UpdateSelectedTypesHistogram(sync_everything, chosen_types); 1358 sync_prefs_.SetKeepEverythingSynced(sync_everything); 1359 1360 failed_datatypes_handler_.OnUserChoseDatatypes(); 1361 ChangePreferredDataTypes(chosen_types); 1362 AcknowledgeSyncedTypes(); 1363 NotifyObservers(); 1364} 1365 1366void ProfileSyncService::ChangePreferredDataTypes( 1367 syncer::ModelTypeSet preferred_types) { 1368 1369 DVLOG(1) << "ChangePreferredDataTypes invoked"; 1370 const syncer::ModelTypeSet registered_types = GetRegisteredDataTypes(); 1371 const syncer::ModelTypeSet registered_preferred_types = 1372 Intersection(registered_types, preferred_types); 1373 sync_prefs_.SetPreferredDataTypes(registered_types, 1374 registered_preferred_types); 1375 1376 // Now reconfigure the DTM. 1377 ReconfigureDatatypeManager(); 1378} 1379 1380syncer::ModelTypeSet ProfileSyncService::GetPreferredDataTypes() const { 1381 const syncer::ModelTypeSet registered_types = GetRegisteredDataTypes(); 1382 const syncer::ModelTypeSet preferred_types = 1383 sync_prefs_.GetPreferredDataTypes(registered_types); 1384 const syncer::ModelTypeSet failed_types = 1385 failed_datatypes_handler_.GetFailedTypes(); 1386 return Difference(preferred_types, failed_types); 1387} 1388 1389syncer::ModelTypeSet ProfileSyncService::GetRegisteredDataTypes() const { 1390 syncer::ModelTypeSet registered_types; 1391 // The data_type_controllers_ are determined by command-line flags; that's 1392 // effectively what controls the values returned here. 1393 for (DataTypeController::TypeMap::const_iterator it = 1394 data_type_controllers_.begin(); 1395 it != data_type_controllers_.end(); ++it) { 1396 registered_types.Put(it->first); 1397 } 1398 return registered_types; 1399} 1400 1401bool ProfileSyncService::IsUsingSecondaryPassphrase() const { 1402 syncer::PassphraseType passphrase_type = GetPassphraseType(); 1403 return passphrase_type == syncer::FROZEN_IMPLICIT_PASSPHRASE || 1404 passphrase_type == syncer::CUSTOM_PASSPHRASE; 1405} 1406 1407syncer::PassphraseType ProfileSyncService::GetPassphraseType() const { 1408 return backend_->GetPassphraseType(); 1409} 1410 1411base::Time ProfileSyncService::GetExplicitPassphraseTime() const { 1412 return backend_->GetExplicitPassphraseTime(); 1413} 1414 1415bool ProfileSyncService::IsCryptographerReady( 1416 const syncer::BaseTransaction* trans) const { 1417 return backend_.get() && backend_->IsCryptographerReady(trans); 1418} 1419 1420SyncBackendHost* ProfileSyncService::GetBackendForTest() { 1421 // We don't check |backend_initialized_|; we assume the test class 1422 // knows what it's doing. 1423 return backend_.get(); 1424} 1425 1426void ProfileSyncService::ConfigureDataTypeManager() { 1427 // Don't configure datatypes if the setup UI is still on the screen - this 1428 // is to help multi-screen setting UIs (like iOS) where they don't want to 1429 // start syncing data until the user is done configuring encryption options, 1430 // etc. ReconfigureDatatypeManager() will get called again once the UI calls 1431 // SetSetupInProgress(false). 1432 if (setup_in_progress_) 1433 return; 1434 1435 bool restart = false; 1436 if (!data_type_manager_.get()) { 1437 restart = true; 1438 data_type_manager_.reset( 1439 factory_->CreateDataTypeManager(debug_info_listener_, 1440 backend_.get(), 1441 &data_type_controllers_, 1442 this)); 1443 1444 // We create the migrator at the same time. 1445 migrator_.reset( 1446 new browser_sync::BackendMigrator( 1447 profile_->GetDebugName(), GetUserShare(), 1448 this, data_type_manager_.get(), 1449 base::Bind(&ProfileSyncService::StartSyncingWithServer, 1450 base::Unretained(this)))); 1451 } 1452 1453 const syncer::ModelTypeSet types = GetPreferredDataTypes(); 1454 if (IsPassphraseRequiredForDecryption()) { 1455 // We need a passphrase still. We don't bother to attempt to configure 1456 // until we receive an OnPassphraseAccepted (which triggers a configure). 1457 DVLOG(1) << "ProfileSyncService::ConfigureDataTypeManager bailing out " 1458 << "because a passphrase required"; 1459 NotifyObservers(); 1460 return; 1461 } 1462 syncer::ConfigureReason reason = syncer::CONFIGURE_REASON_UNKNOWN; 1463 if (!HasSyncSetupCompleted()) { 1464 reason = syncer::CONFIGURE_REASON_NEW_CLIENT; 1465 } else if (restart) { 1466 // Datatype downloads on restart are generally due to newly supported 1467 // datatypes (although it's also possible we're picking up where a failed 1468 // previous configuration left off). 1469 // TODO(sync): consider detecting configuration recovery and setting 1470 // the reason here appropriately. 1471 reason = syncer::CONFIGURE_REASON_NEWLY_ENABLED_DATA_TYPE; 1472 } else { 1473 // The user initiated a reconfiguration (either to add or remove types). 1474 reason = syncer::CONFIGURE_REASON_RECONFIGURATION; 1475 } 1476 1477 data_type_manager_->Configure(types, reason); 1478} 1479 1480syncer::UserShare* ProfileSyncService::GetUserShare() const { 1481 if (backend_.get() && backend_initialized_) { 1482 return backend_->GetUserShare(); 1483 } 1484 NOTREACHED(); 1485 return NULL; 1486} 1487 1488syncer::sessions::SyncSessionSnapshot 1489 ProfileSyncService::GetLastSessionSnapshot() const { 1490 if (backend_.get() && backend_initialized_) { 1491 return backend_->GetLastSessionSnapshot(); 1492 } 1493 NOTREACHED(); 1494 return syncer::sessions::SyncSessionSnapshot(); 1495} 1496 1497bool ProfileSyncService::HasUnsyncedItems() const { 1498 if (backend_.get() && backend_initialized_) { 1499 return backend_->HasUnsyncedItems(); 1500 } 1501 NOTREACHED(); 1502 return false; 1503} 1504 1505browser_sync::BackendMigrator* 1506 ProfileSyncService::GetBackendMigratorForTest() { 1507 return migrator_.get(); 1508} 1509 1510void ProfileSyncService::GetModelSafeRoutingInfo( 1511 syncer::ModelSafeRoutingInfo* out) const { 1512 if (backend_.get() && backend_initialized_) { 1513 backend_->GetModelSafeRoutingInfo(out); 1514 } else { 1515 NOTREACHED(); 1516 } 1517} 1518 1519Value* ProfileSyncService::GetTypeStatusMap() const { 1520 ListValue* result = new ListValue(); 1521 1522 if (!backend_.get() || !backend_initialized_) { 1523 return result; 1524 } 1525 1526 std::vector<syncer::SyncError> errors = 1527 failed_datatypes_handler_.GetAllErrors(); 1528 std::map<ModelType, syncer::SyncError> error_map; 1529 for (std::vector<syncer::SyncError>::iterator it = errors.begin(); 1530 it != errors.end(); ++it) { 1531 error_map[it->type()] = *it; 1532 } 1533 1534 ModelTypeSet active_types; 1535 ModelTypeSet passive_types; 1536 ModelSafeRoutingInfo routing_info; 1537 backend_->GetModelSafeRoutingInfo(&routing_info); 1538 for (ModelSafeRoutingInfo::const_iterator it = routing_info.begin(); 1539 it != routing_info.end(); ++it) { 1540 if (it->second == syncer::GROUP_PASSIVE) { 1541 passive_types.Put(it->first); 1542 } else { 1543 active_types.Put(it->first); 1544 } 1545 } 1546 1547 SyncBackendHost::Status detailed_status = backend_->GetDetailedStatus(); 1548 ModelTypeSet &throttled_types(detailed_status.throttled_types); 1549 1550 ModelTypeSet registered = GetRegisteredDataTypes(); 1551 for (ModelTypeSet::Iterator it = registered.First(); it.Good(); it.Inc()) { 1552 ModelType type = it.Get(); 1553 DictionaryValue* type_status = new DictionaryValue(); 1554 1555 result->Append(type_status); 1556 type_status->SetString("name", ModelTypeToString(type)); 1557 1558 if (error_map.find(type) != error_map.end()) { 1559 const syncer::SyncError &error = error_map.find(type)->second; 1560 DCHECK(error.IsSet()); 1561 std::string error_text = "Error: " + error.location().ToString() + 1562 ", " + error.message(); 1563 type_status->SetString("status", "error"); 1564 type_status->SetString("value", error_text); 1565 } else if (throttled_types.Has(type) && passive_types.Has(type)) { 1566 type_status->SetString("status", "warning"); 1567 type_status->SetString("value", "Passive, Throttled"); 1568 } else if (passive_types.Has(type)) { 1569 type_status->SetString("status", "warning"); 1570 type_status->SetString("value", "Passive"); 1571 } else if (throttled_types.Has(type)) { 1572 type_status->SetString("status", "warning"); 1573 type_status->SetString("value", "Throttled"); 1574 } else if (active_types.Has(type)) { 1575 type_status->SetString("status", "ok"); 1576 type_status->SetString("value", "Active: " + 1577 ModelSafeGroupToString(routing_info[type])); 1578 } else { 1579 type_status->SetString("status", "warning"); 1580 type_status->SetString("value", "Disabled by User"); 1581 } 1582 } 1583 return result; 1584} 1585 1586void ProfileSyncService::ActivateDataType( 1587 syncer::ModelType type, syncer::ModelSafeGroup group, 1588 ChangeProcessor* change_processor) { 1589 if (!backend_.get()) { 1590 NOTREACHED(); 1591 return; 1592 } 1593 DCHECK(backend_initialized_); 1594 backend_->ActivateDataType(type, group, change_processor); 1595} 1596 1597void ProfileSyncService::DeactivateDataType(syncer::ModelType type) { 1598 if (!backend_.get()) 1599 return; 1600 backend_->DeactivateDataType(type); 1601} 1602 1603void ProfileSyncService::ConsumeCachedPassphraseIfPossible() { 1604 // If no cached passphrase, or sync backend hasn't started up yet, just exit. 1605 // If the backend isn't running yet, OnBackendInitialized() will call this 1606 // method again after the backend starts up. 1607 if (cached_passphrase_.empty() || !sync_initialized()) 1608 return; 1609 1610 // Backend is up and running, so we can consume the cached passphrase. 1611 std::string passphrase = cached_passphrase_; 1612 cached_passphrase_.clear(); 1613 1614 // If we need a passphrase to decrypt data, try the cached passphrase. 1615 if (passphrase_required_reason() == syncer::REASON_DECRYPTION) { 1616 if (SetDecryptionPassphrase(passphrase)) { 1617 DVLOG(1) << "Cached passphrase successfully decrypted pending keys"; 1618 return; 1619 } 1620 } 1621 1622 // If we get here, we don't have pending keys (or at least, the passphrase 1623 // doesn't decrypt them) - just try to re-encrypt using the encryption 1624 // passphrase. 1625 if (!IsUsingSecondaryPassphrase()) 1626 SetEncryptionPassphrase(passphrase, IMPLICIT); 1627} 1628 1629void ProfileSyncService::SetEncryptionPassphrase(const std::string& passphrase, 1630 PassphraseType type) { 1631 // This should only be called when the backend has been initialized. 1632 DCHECK(sync_initialized()); 1633 DCHECK(!(type == IMPLICIT && IsUsingSecondaryPassphrase())) << 1634 "Data is already encrypted using an explicit passphrase"; 1635 DCHECK(!(type == EXPLICIT && IsPassphraseRequired())) << 1636 "Cannot switch to an explicit passphrase if a passphrase is required"; 1637 1638 if (type == EXPLICIT) 1639 UMA_HISTOGRAM_BOOLEAN("Sync.CustomPassphrase", true); 1640 1641 DVLOG(1) << "Setting " << (type == EXPLICIT ? "explicit" : "implicit") 1642 << " passphrase for encryption."; 1643 if (passphrase_required_reason_ == syncer::REASON_ENCRYPTION) { 1644 // REASON_ENCRYPTION implies that the cryptographer does not have pending 1645 // keys. Hence, as long as we're not trying to do an invalid passphrase 1646 // change (e.g. explicit -> explicit or explicit -> implicit), we know this 1647 // will succeed. If for some reason a new encryption key arrives via 1648 // sync later, the SBH will trigger another OnPassphraseRequired(). 1649 passphrase_required_reason_ = syncer::REASON_PASSPHRASE_NOT_REQUIRED; 1650 NotifyObservers(); 1651 } 1652 backend_->SetEncryptionPassphrase(passphrase, type == EXPLICIT); 1653} 1654 1655bool ProfileSyncService::SetDecryptionPassphrase( 1656 const std::string& passphrase) { 1657 if (IsPassphraseRequired()) { 1658 DVLOG(1) << "Setting passphrase for decryption."; 1659 return backend_->SetDecryptionPassphrase(passphrase); 1660 } else { 1661 NOTREACHED() << "SetDecryptionPassphrase must not be called when " 1662 "IsPassphraseRequired() is false."; 1663 return false; 1664 } 1665} 1666 1667void ProfileSyncService::EnableEncryptEverything() { 1668 // Tests override sync_initialized() to always return true, so we 1669 // must check that instead of |backend_initialized_|. 1670 // TODO(akalin): Fix the above. :/ 1671 DCHECK(sync_initialized()); 1672 // TODO(atwilson): Persist the encryption_pending_ flag to address the various 1673 // problems around cancelling encryption in the background (crbug.com/119649). 1674 if (!encrypt_everything_) 1675 encryption_pending_ = true; 1676 UMA_HISTOGRAM_BOOLEAN("Sync.EncryptAllData", true); 1677} 1678 1679bool ProfileSyncService::encryption_pending() const { 1680 // We may be called during the setup process before we're 1681 // initialized (via IsEncryptedDatatypeEnabled and 1682 // IsPassphraseRequiredForDecryption). 1683 return encryption_pending_; 1684} 1685 1686bool ProfileSyncService::EncryptEverythingEnabled() const { 1687 DCHECK(backend_initialized_); 1688 return encrypt_everything_ || encryption_pending_; 1689} 1690 1691syncer::ModelTypeSet ProfileSyncService::GetEncryptedDataTypes() const { 1692 DCHECK(encrypted_types_.Has(syncer::PASSWORDS)); 1693 // We may be called during the setup process before we're 1694 // initialized. In this case, we default to the sensitive types. 1695 return encrypted_types_; 1696} 1697 1698void ProfileSyncService::OnSyncManagedPrefChange(bool is_sync_managed) { 1699 NotifyObservers(); 1700 if (is_sync_managed) { 1701 DisableForUser(); 1702 } else if (HasSyncSetupCompleted() && 1703 IsSyncEnabledAndLoggedIn() && 1704 IsSyncTokenAvailable()) { 1705 // Previously-configured sync has been re-enabled, so start sync now. 1706 StartUp(); 1707 } 1708} 1709 1710void ProfileSyncService::Observe(int type, 1711 const content::NotificationSource& source, 1712 const content::NotificationDetails& details) { 1713 switch (type) { 1714 case chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL: { 1715 const GoogleServiceSigninSuccessDetails* successful = 1716 content::Details<const GoogleServiceSigninSuccessDetails>( 1717 details).ptr(); 1718 DCHECK(!successful->password.empty()); 1719 if (!sync_prefs_.IsStartSuppressed()) { 1720 cached_passphrase_ = successful->password; 1721 // Try to consume the passphrase we just cached. If the sync backend 1722 // is not running yet, the passphrase will remain cached until the 1723 // backend starts up. 1724 ConsumeCachedPassphraseIfPossible(); 1725 } 1726#if defined(OS_CHROMEOS) 1727 RefreshSpareBootstrapToken(successful->password); 1728#endif 1729 if (!sync_initialized() || 1730 GetAuthError().state() != AuthError::NONE) { 1731 // Track the fact that we're still waiting for auth to complete. 1732 is_auth_in_progress_ = true; 1733 } 1734 break; 1735 } 1736 case chrome::NOTIFICATION_TOKEN_REQUEST_FAILED: { 1737 const TokenService::TokenRequestFailedDetails& token_details = 1738 *(content::Details<const TokenService::TokenRequestFailedDetails>( 1739 details).ptr()); 1740 if (IsTokenServiceRelevant(token_details.service()) && 1741 !IsSyncTokenAvailable()) { 1742 // The additional check around IsSyncTokenAvailable() above prevents us 1743 // sounding the alarm if we actually have a valid token but a refresh 1744 // attempt by TokenService failed for any variety of reasons (e.g. flaky 1745 // network). It's possible the token we do have is also invalid, but in 1746 // that case we should already have (or can expect) an auth error sent 1747 // from the sync backend. 1748 AuthError error(AuthError::INVALID_GAIA_CREDENTIALS); 1749 UpdateAuthErrorState(error); 1750 } 1751 break; 1752 } 1753 case chrome::NOTIFICATION_TOKEN_AVAILABLE: { 1754 const TokenService::TokenAvailableDetails& token_details = 1755 *(content::Details<const TokenService::TokenAvailableDetails>( 1756 details).ptr()); 1757 if (IsTokenServiceRelevant(token_details.service()) && 1758 IsSyncEnabledAndLoggedIn() && 1759 IsSyncTokenAvailable()) { 1760 if (backend_initialized_) 1761 backend_->UpdateCredentials(GetCredentials()); 1762 else 1763 StartUp(); 1764 } 1765 break; 1766 } 1767 case chrome::NOTIFICATION_TOKEN_LOADING_FINISHED: { 1768 // This notification gets fired when TokenService loads the tokens 1769 // from storage. 1770 if (IsSyncEnabledAndLoggedIn()) { 1771 // Don't start up sync and generate an auth error on auto_start 1772 // platforms as they have their own way to resolve TokenService errors. 1773 // (crbug.com/128592). 1774 if (auto_start_enabled_ && !IsSyncTokenAvailable()) 1775 break; 1776 1777 // Initialize the backend if sync is enabled. If the sync token was 1778 // not loaded, GetCredentials() will generate invalid credentials to 1779 // cause the backend to generate an auth error (crbug.com/121755). 1780 if (backend_initialized_) 1781 backend_->UpdateCredentials(GetCredentials()); 1782 else 1783 StartUp(); 1784 } 1785 break; 1786 } 1787 default: { 1788 NOTREACHED(); 1789 } 1790 } 1791} 1792 1793void ProfileSyncService::AddObserver(Observer* observer) { 1794 observers_.AddObserver(observer); 1795} 1796 1797void ProfileSyncService::RemoveObserver(Observer* observer) { 1798 observers_.RemoveObserver(observer); 1799} 1800 1801bool ProfileSyncService::HasObserver(Observer* observer) const { 1802 return observers_.HasObserver(observer); 1803} 1804 1805base::WeakPtr<syncer::JsController> ProfileSyncService::GetJsController() { 1806 return sync_js_controller_.AsWeakPtr(); 1807} 1808 1809void ProfileSyncService::SyncEvent(SyncEventCodes code) { 1810 UMA_HISTOGRAM_ENUMERATION("Sync.EventCodes", code, MAX_SYNC_EVENT_CODE); 1811} 1812 1813// static 1814bool ProfileSyncService::IsSyncEnabled() { 1815 // We have switches::kEnableSync just in case we need to change back to 1816 // sync-disabled-by-default on a platform. 1817 return !CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableSync); 1818} 1819 1820bool ProfileSyncService::IsManaged() const { 1821 return sync_prefs_.IsManaged(); 1822} 1823 1824bool ProfileSyncService::ShouldPushChanges() { 1825 // True only after all bootstrapping has succeeded: the sync backend 1826 // is initialized, all enabled data types are consistent with one 1827 // another, and no unrecoverable error has transpired. 1828 if (HasUnrecoverableError()) 1829 return false; 1830 1831 if (!data_type_manager_.get()) 1832 return false; 1833 1834 return data_type_manager_->state() == DataTypeManager::CONFIGURED; 1835} 1836 1837void ProfileSyncService::StopAndSuppress() { 1838 sync_prefs_.SetStartSuppressed(true); 1839 ShutdownImpl(false); 1840} 1841 1842void ProfileSyncService::UnsuppressAndStart() { 1843 DCHECK(profile_); 1844 sync_prefs_.SetStartSuppressed(false); 1845 // Set username in SigninManager, as SigninManager::OnGetUserInfoSuccess 1846 // is never called for some clients. 1847 if (signin_ && signin_->GetAuthenticatedUsername().empty()) { 1848 signin_->SetAuthenticatedUsername(sync_prefs_.GetGoogleServicesUsername()); 1849 } 1850 TryStart(); 1851} 1852 1853void ProfileSyncService::AcknowledgeSyncedTypes() { 1854 sync_prefs_.AcknowledgeSyncedTypes(GetRegisteredDataTypes()); 1855} 1856 1857void ProfileSyncService::ReconfigureDatatypeManager() { 1858 // If we haven't initialized yet, don't configure the DTM as it could cause 1859 // association to start before a Directory has even been created. 1860 if (backend_initialized_) { 1861 DCHECK(backend_.get()); 1862 ConfigureDataTypeManager(); 1863 } else if (HasUnrecoverableError()) { 1864 // There is nothing more to configure. So inform the listeners, 1865 NotifyObservers(); 1866 1867 DVLOG(1) << "ConfigureDataTypeManager not invoked because of an " 1868 << "Unrecoverable error."; 1869 } else { 1870 DVLOG(0) << "ConfigureDataTypeManager not invoked because backend is not " 1871 << "initialized"; 1872 } 1873} 1874 1875const FailedDatatypesHandler& ProfileSyncService::failed_datatypes_handler() 1876 const { 1877 return failed_datatypes_handler_; 1878} 1879 1880void ProfileSyncService::OnInternalUnrecoverableError( 1881 const tracked_objects::Location& from_here, 1882 const std::string& message, 1883 bool delete_sync_database, 1884 UnrecoverableErrorReason reason) { 1885 DCHECK(!HasUnrecoverableError()); 1886 unrecoverable_error_reason_ = reason; 1887 OnUnrecoverableErrorImpl(from_here, message, delete_sync_database); 1888} 1889 1890void ProfileSyncService::UpdateInvalidatorRegistrarState() { 1891 const syncer::InvalidatorState effective_state = 1892 backend_initialized_ ? 1893 invalidator_state_ : syncer::TRANSIENT_INVALIDATION_ERROR; 1894 invalidator_registrar_->UpdateInvalidatorState(effective_state); 1895} 1896 1897void ProfileSyncService::ResetForTest() { 1898 Profile* profile = profile_; 1899 SigninManager* signin = SigninManagerFactory::GetForProfile(profile); 1900 ProfileSyncService::StartBehavior behavior = 1901 browser_defaults::kSyncAutoStarts ? ProfileSyncService::AUTO_START 1902 : ProfileSyncService::MANUAL_START; 1903 1904 // We call the destructor and placement new here because we want to explicitly 1905 // recreate a new ProfileSyncService instance at the same memory location as 1906 // the old one. Doing so is fine because this code is run only from within 1907 // integration tests, and the message loop is not running at this point. 1908 // See http://stackoverflow.com/questions/6224121/is-new-this-myclass-undefined-behaviour-after-directly-calling-the-destru. 1909 ProfileSyncService* old_this = this; 1910 this->~ProfileSyncService(); 1911 new(old_this) ProfileSyncService( 1912 new ProfileSyncComponentsFactoryImpl(profile, 1913 CommandLine::ForCurrentProcess()), 1914 profile, 1915 signin, 1916 behavior); 1917} 1918