Revert 168224 - Update V8 to version 3.15.4.
[chromium-blink-merge.git] / chrome / browser / sync / profile_sync_service.cc
blob9b8e922e9d28ddcc8dfe68a01b833e05e18ef0b0
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.
5 #include "chrome/browser/sync/profile_sync_service.h"
7 #include <cstddef>
8 #include <map>
9 #include <set>
10 #include <utility>
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"
76 using browser_sync::ChangeProcessor;
77 using browser_sync::DataTypeController;
78 using browser_sync::DataTypeManager;
79 using browser_sync::SyncBackendHost;
80 using syncer::ModelType;
81 using syncer::ModelTypeSet;
82 using syncer::JsBackend;
83 using syncer::JsController;
84 using syncer::JsEventDetails;
85 using syncer::JsEventHandler;
86 using syncer::ModelSafeRoutingInfo;
87 using syncer::SyncCredentials;
88 using syncer::SyncProtocolError;
89 using syncer::WeakHandle;
91 typedef GoogleServiceAuthError AuthError;
93 const char* ProfileSyncService::kSyncServerUrl =
94 "https://clients4.google.com/chrome-sync";
96 const char* ProfileSyncService::kDevServerUrl =
97 "https://clients4.google.com/chrome-sync/dev";
99 static const int kSyncClearDataTimeoutInSeconds = 60; // 1 minute.
101 static const char* kRelevantTokenServices[] = {
102 GaiaConstants::kSyncService
104 static const int kRelevantTokenServicesCount =
105 arraysize(kRelevantTokenServices);
107 static const char* kSyncUnrecoverableErrorHistogram =
108 "Sync.UnrecoverableErrors";
110 // Helper to check if the given token service is relevant for sync.
111 static bool IsTokenServiceRelevant(const std::string& service) {
112 for (int i = 0; i < kRelevantTokenServicesCount; ++i) {
113 if (service == kRelevantTokenServices[i])
114 return true;
116 return false;
119 bool ShouldShowActionOnUI(
120 const syncer::SyncProtocolError& error) {
121 return (error.action != syncer::UNKNOWN_ACTION &&
122 error.action != syncer::DISABLE_SYNC_ON_CLIENT);
125 ProfileSyncService::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);
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.
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);
170 #endif
173 ProfileSyncService::~ProfileSyncService() {
174 sync_prefs_.RemoveSyncPrefObserver(this);
175 // Shutdown() should have been called before destruction.
176 CHECK(!backend_initialized_);
179 bool ProfileSyncService::IsSyncEnabledAndLoggedIn() {
180 // Exit if sync is disabled.
181 if (IsManaged() || sync_prefs_.IsStartSuppressed())
182 return false;
184 // Sync is logged in if there is a non-empty authenticated username.
185 return !signin_->GetAuthenticatedUsername().empty();
188 bool ProfileSyncService::IsSyncTokenAvailable() {
189 TokenService* token_service = TokenServiceFactory::GetForProfile(profile_);
190 if (!token_service)
191 return false;
192 return token_service->HasTokenForService(GaiaConstants::kSyncService);
195 void ProfileSyncService::Initialize() {
196 DCHECK(!invalidator_registrar_.get());
197 invalidator_registrar_.reset(new syncer::InvalidatorRegistrar());
199 InitSettings();
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();
206 sync_prefs_.AddSyncPrefObserver(this);
208 // For now, the only thing we can do through policy is to turn sync off.
209 if (IsManaged()) {
210 DisableForUser();
211 return;
214 RegisterAuthNotifications();
216 if (!HasSyncSetupCompleted() || signin_->GetAuthenticatedUsername().empty()) {
217 // Clean up in case of previous crash / setup abort / signout.
218 DisableForUser();
221 TrySyncDatatypePrefRecovery();
223 TryStart();
226 void ProfileSyncService::TrySyncDatatypePrefRecovery() {
227 DCHECK(!sync_initialized());
228 if (!HasSyncSetupCompleted())
229 return;
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;
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;
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);
260 void 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();
286 void ProfileSyncService::StartSyncingWithServer() {
287 if (backend_.get())
288 backend_->StartSyncingWithServer();
291 void 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_));
307 void 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;
314 browser_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;
322 return static_cast<browser_sync::SessionDataTypeController*>(
323 data_type_controllers_.find(
324 syncer::SESSIONS)->second.get())->GetModelAssociator();
327 void 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();
335 void ProfileSyncService::InitSettings() {
336 const CommandLine& command_line = *CommandLine::ForCurrentProcess();
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;
355 SyncCredentials 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";
370 return credentials;
373 void ProfileSyncService::InitializeBackend(bool delete_stale_data) {
374 if (!backend_.get()) {
375 NOTREACHED();
376 return;
379 SyncCredentials credentials = GetCredentials();
381 scoped_refptr<net::URLRequestContextGetter> request_context_getter(
382 profile_->GetRequestContext());
384 if (delete_stale_data)
385 ClearStaleErrors();
387 backend_unrecoverable_error_handler_.reset(
388 new browser_sync::BackendUnrecoverableErrorHandler(
389 MakeWeakHandle(weak_factory_.GetWeakPtr())));
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);
402 void ProfileSyncService::CreateBackend() {
403 backend_.reset(
404 new SyncBackendHost(profile_->GetDebugName(),
405 profile_, sync_prefs_.AsWeakPtr(),
406 invalidator_storage_.AsWeakPtr()));
409 bool 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();
418 void ProfileSyncService::OnSyncConfigureDone(
419 DataTypeManager::ConfigureResult result) {
420 if (failed_datatypes_handler_.UpdateFailedDatatypes(result.failed_data_types,
421 FailedDatatypesHandler::STARTUP)) {
422 ReconfigureDatatypeManager();
426 void 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);
444 NotifyObservers();
448 void ProfileSyncService::StartUp() {
449 // Don't start up multiple times.
450 if (backend_.get()) {
451 DVLOG(1) << "Skipping bringing up backend host.";
452 return;
455 DCHECK(IsSyncEnabledAndLoggedIn());
457 last_synced_time_ = sync_prefs_.GetLastSyncedTime();
458 start_up_time_ = base::Time::Now();
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());
466 #endif
467 CreateBackend();
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());
474 // |backend_| may end up being NULL here in tests (in synchronous
475 // initialization mode).
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());
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());
494 void ProfileSyncService::RegisterInvalidationHandler(
495 syncer::InvalidationHandler* handler) {
496 invalidator_registrar_->RegisterHandler(handler);
499 void ProfileSyncService::UpdateRegisteredInvalidationIds(
500 syncer::InvalidationHandler* handler,
501 const syncer::ObjectIdSet& ids) {
502 invalidator_registrar_->UpdateRegisteredIds(handler, ids);
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());
512 void ProfileSyncService::UnregisterInvalidationHandler(
513 syncer::InvalidationHandler* handler) {
514 invalidator_registrar_->UnregisterHandler(handler);
517 syncer::InvalidatorState ProfileSyncService::GetInvalidatorState() const {
518 return invalidator_registrar_->GetInvalidatorState();
521 void ProfileSyncService::EmitInvalidationForTest(
522 const invalidation::ObjectId& id,
523 const std::string& payload) {
524 syncer::ObjectIdSet notify_ids;
525 notify_ids.insert(id);
527 const syncer::ObjectIdInvalidationMap& invalidation_map =
528 ObjectIdSetToInvalidationMap(notify_ids, payload);
529 OnIncomingInvalidation(invalidation_map, syncer::REMOTE_INVALIDATION);
532 void 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();
542 // Reset |invalidator_registrar_| first so that ShutdownImpl cannot
543 // use it.
544 invalidator_registrar_.reset();
546 ShutdownImpl(false);
549 void 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();
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();
571 data_type_manager_.reset();
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>());
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);
585 doomed_backend.reset();
587 base::TimeDelta shutdown_time = base::Time::Now() - shutdown_start_time;
588 UMA_HISTOGRAM_TIMES("Sync.Shutdown.BackendDestroyedTime", shutdown_time);
590 weak_factory_.InvalidateWeakPtrs();
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();
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);
614 void 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);
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();
628 NotifyObservers();
631 bool ProfileSyncService::HasSyncSetupCompleted() const {
632 return sync_prefs_.HasSyncSetupCompleted();
635 void ProfileSyncService::SetSyncSetupCompleted() {
636 sync_prefs_.SetSyncSetupCompleted();
639 void ProfileSyncService::UpdateLastSyncedTime() {
640 last_synced_time_ = base::Time::Now();
641 sync_prefs_.SetLastSyncedTime(last_synced_time_);
644 void 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());
652 void ProfileSyncService::ClearStaleErrors() {
653 ClearUnrecoverableError();
654 last_actionable_error_ = SyncProtocolError();
657 void ProfileSyncService::ClearUnrecoverableError() {
658 unrecoverable_error_reason_ = ERROR_REASON_UNSET;
659 unrecoverable_error_message_.clear();
660 unrecoverable_error_location_ = tracked_objects::Location();
663 // static
664 // TODO(sync): Consider having syncer::Experiments provide this.
665 std::string ProfileSyncService::GetExperimentNameForDataType(
666 syncer::ModelType data_type) {
667 NOTREACHED();
668 return "";
671 void ProfileSyncService::RegisterNewDataType(syncer::ModelType data_type) {
672 if (data_type_controllers_.count(data_type) > 0)
673 return;
674 NOTREACHED();
677 // An invariant has been violated. Transition to an error state where we try
678 // to do as little work as possible, to avoid further corruption or crashes.
679 void ProfileSyncService::OnUnrecoverableError(
680 const tracked_objects::Location& from_here,
681 const std::string& message) {
682 // Unrecoverable errors that arrive via the syncer::UnrecoverableErrorHandler
683 // interface are assumed to originate within the syncer.
684 unrecoverable_error_reason_ = ERROR_REASON_SYNCER;
685 OnUnrecoverableErrorImpl(from_here, message, true);
688 void ProfileSyncService::OnUnrecoverableErrorImpl(
689 const tracked_objects::Location& from_here,
690 const std::string& message,
691 bool delete_sync_database) {
692 DCHECK(HasUnrecoverableError());
693 unrecoverable_error_message_ = message;
694 unrecoverable_error_location_ = from_here;
696 UMA_HISTOGRAM_ENUMERATION(kSyncUnrecoverableErrorHistogram,
697 unrecoverable_error_reason_,
698 ERROR_REASON_LIMIT);
699 NotifyObservers();
700 std::string location;
701 from_here.Write(true, true, &location);
702 LOG(ERROR)
703 << "Unrecoverable error detected at " << location
704 << " -- ProfileSyncService unusable: " << message;
706 // Shut all data types down.
707 MessageLoop::current()->PostTask(FROM_HERE,
708 base::Bind(&ProfileSyncService::ShutdownImpl, weak_factory_.GetWeakPtr(),
709 delete_sync_database));
712 void ProfileSyncService::DisableBrokenDatatype(
713 syncer::ModelType type,
714 const tracked_objects::Location& from_here,
715 std::string message) {
716 // First deactivate the type so that no further server changes are
717 // passed onto the change processor.
718 DeactivateDataType(type);
720 syncer::SyncError error(from_here, message, type);
722 std::list<syncer::SyncError> errors;
723 errors.push_back(error);
725 // Update this before posting a task. So if a configure happens before
726 // the task that we are going to post, this type would still be disabled.
727 failed_datatypes_handler_.UpdateFailedDatatypes(errors,
728 FailedDatatypesHandler::RUNTIME);
730 MessageLoop::current()->PostTask(FROM_HERE,
731 base::Bind(&ProfileSyncService::ReconfigureDatatypeManager,
732 weak_factory_.GetWeakPtr()));
735 void ProfileSyncService::OnInvalidatorStateChange(
736 syncer::InvalidatorState state) {
737 invalidator_state_ = state;
738 UpdateInvalidatorRegistrarState();
741 void ProfileSyncService::OnIncomingInvalidation(
742 const syncer::ObjectIdInvalidationMap& invalidation_map,
743 syncer::IncomingInvalidationSource source) {
744 invalidator_registrar_->DispatchInvalidationsToHandlers(invalidation_map,
745 source);
748 void ProfileSyncService::OnBackendInitialized(
749 const syncer::WeakHandle<syncer::JsBackend>& js_backend,
750 const syncer::WeakHandle<syncer::DataTypeDebugInfoListener>&
751 debug_info_listener,
752 bool success) {
753 is_first_time_sync_configure_ = !HasSyncSetupCompleted();
755 if (is_first_time_sync_configure_) {
756 UMA_HISTOGRAM_BOOLEAN("Sync.BackendInitializeFirstTimeSuccess", success);
757 } else {
758 UMA_HISTOGRAM_BOOLEAN("Sync.BackendInitializeRestoreSuccess", success);
761 if (!start_up_time_.is_null()) {
762 base::Time on_backend_initialized_time = base::Time::Now();
763 base::TimeDelta delta = on_backend_initialized_time - start_up_time_;
764 if (is_first_time_sync_configure_) {
765 UMA_HISTOGRAM_LONG_TIMES("Sync.BackendInitializeFirstTime", delta);
766 } else {
767 UMA_HISTOGRAM_LONG_TIMES("Sync.BackendInitializeRestoreTime", delta);
769 start_up_time_ = base::Time();
772 if (!success) {
773 // Something went unexpectedly wrong. Play it safe: stop syncing at once
774 // and surface error UI to alert the user sync has stopped.
775 // Keep the directory around for now so that on restart we will retry
776 // again and potentially succeed in presence of transient file IO failures
777 // or permissions issues, etc.
779 // TODO(rlarocque): Consider making this UnrecoverableError less special.
780 // Unlike every other UnrecoverableError, it does not delete our sync data.
781 // This exception made sense at the time it was implemented, but our new
782 // directory corruption recovery mechanism makes it obsolete. By the time
783 // we get here, we will have already tried and failed to delete the
784 // directory. It would be no big deal if we tried to delete it again.
785 OnInternalUnrecoverableError(FROM_HERE,
786 "BackendInitialize failure",
787 false,
788 ERROR_REASON_BACKEND_INIT_FAILURE);
789 return;
792 backend_initialized_ = true;
793 UpdateInvalidatorRegistrarState();
795 sync_js_controller_.AttachJsBackend(js_backend);
796 debug_info_listener_ = debug_info_listener;
798 // If we have a cached passphrase use it to decrypt/encrypt data now that the
799 // backend is initialized. We want to call this before notifying observers in
800 // case this operation affects the "passphrase required" status.
801 ConsumeCachedPassphraseIfPossible();
803 // The very first time the backend initializes is effectively the first time
804 // we can say we successfully "synced". last_synced_time_ will only be null
805 // in this case, because the pref wasn't restored on StartUp.
806 if (last_synced_time_.is_null()) {
807 UpdateLastSyncedTime();
809 NotifyObservers();
811 if (auto_start_enabled_ && !FirstSetupInProgress()) {
812 // Backend is initialized but we're not in sync setup, so this must be an
813 // autostart - mark our sync setup as completed and we'll start syncing
814 // below.
815 SetSyncSetupCompleted();
816 NotifyObservers();
819 if (HasSyncSetupCompleted()) {
820 ConfigureDataTypeManager();
821 } else {
822 DCHECK(FirstSetupInProgress());
826 void ProfileSyncService::OnSyncCycleCompleted() {
827 UpdateLastSyncedTime();
828 if (GetSessionModelAssociator()) {
829 // Trigger garbage collection of old sessions now that we've downloaded
830 // any new session data. TODO(zea): Have this be a notification the session
831 // model associator listens too. Also consider somehow plumbing the current
832 // server time as last reported by CheckServerReachable, so we don't have to
833 // rely on the local clock, which may be off significantly.
834 MessageLoop::current()->PostTask(FROM_HERE,
835 base::Bind(&browser_sync::SessionModelAssociator::DeleteStaleSessions,
836 GetSessionModelAssociator()->AsWeakPtr()));
838 DVLOG(2) << "Notifying observers sync cycle completed";
839 NotifyObservers();
842 void ProfileSyncService::OnExperimentsChanged(
843 const syncer::Experiments& experiments) {
844 if (current_experiments.Matches(experiments))
845 return;
847 // If this is a first time sync for a client, this will be called before
848 // OnBackendInitialized() to ensure the new datatypes are available at sync
849 // setup. As a result, the migrator won't exist yet. This is fine because for
850 // first time sync cases we're only concerned with making the datatype
851 // available.
852 if (migrator_.get() &&
853 migrator_->state() != browser_sync::BackendMigrator::IDLE) {
854 DVLOG(1) << "Dropping OnExperimentsChanged due to migrator busy.";
855 return;
858 const syncer::ModelTypeSet registered_types = GetRegisteredDataTypes();
859 syncer::ModelTypeSet to_add;
860 const syncer::ModelTypeSet to_register =
861 Difference(to_add, registered_types);
862 DVLOG(2) << "OnExperimentsChanged called with types: "
863 << syncer::ModelTypeSetToString(to_add);
864 DVLOG(2) << "Enabling types: " << syncer::ModelTypeSetToString(to_register);
866 for (syncer::ModelTypeSet::Iterator it = to_register.First();
867 it.Good(); it.Inc()) {
868 // Received notice to enable experimental type. Check if the type is
869 // registered, and if not register a new datatype controller.
870 RegisterNewDataType(it.Get());
871 // Enable the about:flags switch for the experimental type so we don't have
872 // to always perform this reconfiguration. Once we set this, the type will
873 // remain registered on restart, so we will no longer go down this code
874 // path.
875 std::string experiment_name = GetExperimentNameForDataType(it.Get());
876 if (experiment_name.empty())
877 continue;
878 about_flags::SetExperimentEnabled(g_browser_process->local_state(),
879 experiment_name,
880 true);
883 // Check if the user has "Keep Everything Synced" enabled. If so, we want
884 // to turn on all experimental types if they're not already on. Otherwise we
885 // leave them off.
886 // Note: if any types are already registered, we don't turn them on. This
887 // covers the case where we're already in the process of reconfiguring
888 // to turn an experimental type on.
889 if (sync_prefs_.HasKeepEverythingSynced()) {
890 // Mark all data types as preferred.
891 sync_prefs_.SetPreferredDataTypes(registered_types, registered_types);
893 // Only automatically turn on types if we have already finished set up.
894 // Otherwise, just leave the experimental types on by default.
895 if (!to_register.Empty() && HasSyncSetupCompleted() && migrator_.get()) {
896 DVLOG(1) << "Dynamically enabling new datatypes: "
897 << syncer::ModelTypeSetToString(to_register);
898 OnMigrationNeededForTypes(to_register);
902 // Now enable any non-datatype features.
903 if (experiments.sync_tab_favicons) {
904 DVLOG(1) << "Enabling syncing of tab favicons.";
905 about_flags::SetExperimentEnabled(g_browser_process->local_state(),
906 "sync-tab-favicons",
907 true);
908 #if defined(OS_ANDROID)
909 // Android does not support about:flags and experiments, so we need to force
910 // setting the experiments as command line switches.
911 CommandLine::ForCurrentProcess()->AppendSwitch(switches::kSyncTabFavicons);
912 #endif
915 if (experiments.keystore_encryption) {
916 about_flags::SetExperimentEnabled(g_browser_process->local_state(),
917 syncer::kKeystoreEncryptionFlag,
918 true);
921 current_experiments = experiments;
924 void ProfileSyncService::UpdateAuthErrorState(const AuthError& error) {
925 is_auth_in_progress_ = false;
926 last_auth_error_ = error;
928 // Fan the notification out to interested UI-thread components.
929 NotifyObservers();
932 namespace {
934 AuthError ConnectionStatusToAuthError(
935 syncer::ConnectionStatus status) {
936 switch (status) {
937 case syncer::CONNECTION_OK:
938 return AuthError::None();
939 break;
940 case syncer::CONNECTION_AUTH_ERROR:
941 return AuthError(AuthError::INVALID_GAIA_CREDENTIALS);
942 break;
943 case syncer::CONNECTION_SERVER_ERROR:
944 return AuthError(AuthError::CONNECTION_FAILED);
945 break;
946 default:
947 NOTREACHED();
948 return AuthError(AuthError::CONNECTION_FAILED);
952 } // namespace
954 void ProfileSyncService::OnConnectionStatusChange(
955 syncer::ConnectionStatus status) {
956 UpdateAuthErrorState(ConnectionStatusToAuthError(status));
959 void ProfileSyncService::OnStopSyncingPermanently() {
960 UpdateAuthErrorState(AuthError(AuthError::SERVICE_UNAVAILABLE));
961 sync_prefs_.SetStartSuppressed(true);
962 DisableForUser();
965 void ProfileSyncService::OnPassphraseRequired(
966 syncer::PassphraseRequiredReason reason,
967 const sync_pb::EncryptedData& pending_keys) {
968 DCHECK(backend_.get());
969 DCHECK(backend_->IsNigoriEnabled());
971 // TODO(lipalani) : add this check to other locations as well.
972 if (HasUnrecoverableError()) {
973 // When unrecoverable error is detected we post a task to shutdown the
974 // backend. The task might not have executed yet.
975 return;
978 DVLOG(1) << "Passphrase required with reason: "
979 << syncer::PassphraseRequiredReasonToString(reason);
980 passphrase_required_reason_ = reason;
982 // Notify observers that the passphrase status may have changed.
983 NotifyObservers();
986 void ProfileSyncService::OnPassphraseAccepted() {
987 DVLOG(1) << "Received OnPassphraseAccepted.";
988 // If we are not using an explicit passphrase, and we have a cache of the gaia
989 // password, use it for encryption at this point.
990 DCHECK(cached_passphrase_.empty()) <<
991 "Passphrase no longer required but there is still a cached passphrase";
993 // Reset passphrase_required_reason_ since we know we no longer require the
994 // passphrase. We do this here rather than down in ResolvePassphraseRequired()
995 // because that can be called by OnPassphraseRequired() if no encrypted data
996 // types are enabled, and we don't want to clobber the true passphrase error.
997 passphrase_required_reason_ = syncer::REASON_PASSPHRASE_NOT_REQUIRED;
999 // Make sure the data types that depend on the passphrase are started at
1000 // this time.
1001 const syncer::ModelTypeSet types = GetPreferredDataTypes();
1003 if (data_type_manager_.get()) {
1004 // Unblock the data type manager if necessary.
1005 data_type_manager_->Configure(types,
1006 syncer::CONFIGURE_REASON_RECONFIGURATION);
1009 NotifyObservers();
1012 void ProfileSyncService::OnEncryptedTypesChanged(
1013 syncer::ModelTypeSet encrypted_types,
1014 bool encrypt_everything) {
1015 encrypted_types_ = encrypted_types;
1016 encrypt_everything_ = encrypt_everything;
1017 DVLOG(1) << "Encrypted types changed to "
1018 << syncer::ModelTypeSetToString(encrypted_types_)
1019 << " (encrypt everything is set to "
1020 << (encrypt_everything_ ? "true" : "false") << ")";
1021 DCHECK(encrypted_types_.Has(syncer::PASSWORDS));
1024 void ProfileSyncService::OnEncryptionComplete() {
1025 DVLOG(1) << "Encryption complete";
1026 if (encryption_pending_ && encrypt_everything_) {
1027 encryption_pending_ = false;
1028 // This is to nudge the integration tests when encryption is
1029 // finished.
1030 NotifyObservers();
1034 void ProfileSyncService::OnMigrationNeededForTypes(
1035 syncer::ModelTypeSet types) {
1036 DCHECK(backend_initialized_);
1037 DCHECK(data_type_manager_.get());
1039 // Migrator must be valid, because we don't sync until it is created and this
1040 // callback originates from a sync cycle.
1041 migrator_->MigrateTypes(types);
1044 void ProfileSyncService::OnActionableError(const SyncProtocolError& error) {
1045 last_actionable_error_ = error;
1046 DCHECK_NE(last_actionable_error_.action,
1047 syncer::UNKNOWN_ACTION);
1048 switch (error.action) {
1049 case syncer::UPGRADE_CLIENT:
1050 case syncer::CLEAR_USER_DATA_AND_RESYNC:
1051 case syncer::ENABLE_SYNC_ON_ACCOUNT:
1052 case syncer::STOP_AND_RESTART_SYNC:
1053 // TODO(lipalani) : if setup in progress we want to display these
1054 // actions in the popup. The current experience might not be optimal for
1055 // the user. We just dismiss the dialog.
1056 if (setup_in_progress_) {
1057 OnStopSyncingPermanently();
1058 expect_sync_configuration_aborted_ = true;
1060 // Trigger an unrecoverable error to stop syncing.
1061 OnInternalUnrecoverableError(FROM_HERE,
1062 last_actionable_error_.error_description,
1063 true,
1064 ERROR_REASON_ACTIONABLE_ERROR);
1065 break;
1066 case syncer::DISABLE_SYNC_ON_CLIENT:
1067 OnStopSyncingPermanently();
1068 break;
1069 default:
1070 NOTREACHED();
1072 NotifyObservers();
1075 void ProfileSyncService::OnConfigureBlocked() {
1076 NotifyObservers();
1079 void ProfileSyncService::OnConfigureDone(
1080 const browser_sync::DataTypeManager::ConfigureResult& result) {
1081 // We should have cleared our cached passphrase before we get here (in
1082 // OnBackendInitialized()).
1083 DCHECK(cached_passphrase_.empty());
1085 if (!sync_configure_start_time_.is_null()) {
1086 if (result.status == DataTypeManager::OK ||
1087 result.status == DataTypeManager::PARTIAL_SUCCESS) {
1088 base::Time sync_configure_stop_time = base::Time::Now();
1089 base::TimeDelta delta = sync_configure_stop_time -
1090 sync_configure_start_time_;
1091 if (is_first_time_sync_configure_) {
1092 UMA_HISTOGRAM_LONG_TIMES("Sync.ServiceInitialConfigureTime", delta);
1093 } else {
1094 UMA_HISTOGRAM_LONG_TIMES("Sync.ServiceSubsequentConfigureTime",
1095 delta);
1098 sync_configure_start_time_ = base::Time();
1101 // Notify listeners that configuration is done.
1102 content::NotificationService::current()->Notify(
1103 chrome::NOTIFICATION_SYNC_CONFIGURE_DONE,
1104 content::Source<ProfileSyncService>(this),
1105 content::NotificationService::NoDetails());
1107 configure_status_ = result.status;
1108 DVLOG(1) << "PSS OnConfigureDone called with status: " << configure_status_;
1109 // The possible status values:
1110 // ABORT - Configuration was aborted. This is not an error, if
1111 // initiated by user.
1112 // OK - Everything succeeded.
1113 // PARTIAL_SUCCESS - Some datatypes failed to start.
1114 // Everything else is an UnrecoverableError. So treat it as such.
1116 // First handle the abort case.
1117 if (configure_status_ == DataTypeManager::ABORTED &&
1118 expect_sync_configuration_aborted_) {
1119 DVLOG(0) << "ProfileSyncService::Observe Sync Configure aborted";
1120 expect_sync_configuration_aborted_ = false;
1121 return;
1124 // Handle unrecoverable error.
1125 if (configure_status_ != DataTypeManager::OK &&
1126 configure_status_ != DataTypeManager::PARTIAL_SUCCESS) {
1127 // Something catastrophic had happened. We should only have one
1128 // error representing it.
1129 DCHECK_EQ(result.failed_data_types.size(),
1130 static_cast<unsigned int>(1));
1131 syncer::SyncError error = result.failed_data_types.front();
1132 DCHECK(error.IsSet());
1133 std::string message =
1134 "Sync configuration failed with status " +
1135 DataTypeManager::ConfigureStatusToString(configure_status_) +
1136 " during " + syncer::ModelTypeToString(error.type()) +
1137 ": " + error.message();
1138 LOG(ERROR) << "ProfileSyncService error: "
1139 << message;
1140 OnInternalUnrecoverableError(error.location(),
1141 message,
1142 true,
1143 ERROR_REASON_CONFIGURATION_FAILURE);
1144 return;
1147 // Now handle partial success and full success.
1148 MessageLoop::current()->PostTask(FROM_HERE,
1149 base::Bind(&ProfileSyncService::OnSyncConfigureDone,
1150 weak_factory_.GetWeakPtr(), result));
1152 // We should never get in a state where we have no encrypted datatypes
1153 // enabled, and yet we still think we require a passphrase for decryption.
1154 DCHECK(!(IsPassphraseRequiredForDecryption() &&
1155 !IsEncryptedDatatypeEnabled()));
1157 // This must be done before we start syncing with the server to avoid
1158 // sending unencrypted data up on a first time sync.
1159 if (encryption_pending_)
1160 backend_->EnableEncryptEverything();
1161 NotifyObservers();
1163 if (migrator_.get() &&
1164 migrator_->state() != browser_sync::BackendMigrator::IDLE) {
1165 // Migration in progress. Let the migrator know we just finished
1166 // configuring something. It will be up to the migrator to call
1167 // StartSyncingWithServer() if migration is now finished.
1168 migrator_->OnConfigureDone(result);
1169 } else {
1170 StartSyncingWithServer();
1174 void ProfileSyncService::OnConfigureRetry() {
1175 // We should have cleared our cached passphrase before we get here (in
1176 // OnBackendInitialized()).
1177 DCHECK(cached_passphrase_.empty());
1179 OnSyncConfigureRetry();
1182 void ProfileSyncService::OnConfigureStart() {
1183 sync_configure_start_time_ = base::Time::Now();
1184 content::NotificationService::current()->Notify(
1185 chrome::NOTIFICATION_SYNC_CONFIGURE_START,
1186 content::Source<ProfileSyncService>(this),
1187 content::NotificationService::NoDetails());
1188 NotifyObservers();
1191 std::string ProfileSyncService::QuerySyncStatusSummary() {
1192 if (HasUnrecoverableError()) {
1193 return "Unrecoverable error detected";
1194 } else if (!backend_.get()) {
1195 return "Syncing not enabled";
1196 } else if (backend_.get() && !HasSyncSetupCompleted()) {
1197 return "First time sync setup incomplete";
1198 } else if (backend_.get() && HasSyncSetupCompleted() &&
1199 data_type_manager_.get() &&
1200 data_type_manager_->state() != DataTypeManager::CONFIGURED) {
1201 return "Datatypes not fully initialized";
1202 } else if (ShouldPushChanges()) {
1203 return "Sync service initialized";
1204 } else {
1205 return "Status unknown: Internal error?";
1209 bool ProfileSyncService::QueryDetailedSyncStatus(
1210 SyncBackendHost::Status* result) {
1211 if (backend_.get() && backend_initialized_) {
1212 *result = backend_->GetDetailedStatus();
1213 return true;
1214 } else {
1215 SyncBackendHost::Status status;
1216 status.sync_protocol_error = last_actionable_error_;
1217 *result = status;
1218 return false;
1222 const AuthError& ProfileSyncService::GetAuthError() const {
1223 return last_auth_error_;
1226 bool ProfileSyncService::FirstSetupInProgress() const {
1227 return !HasSyncSetupCompleted() && setup_in_progress_;
1230 void ProfileSyncService::SetSetupInProgress(bool setup_in_progress) {
1231 bool was_in_progress = setup_in_progress_;
1232 setup_in_progress_ = setup_in_progress;
1233 if (!setup_in_progress && was_in_progress) {
1234 if (sync_initialized()) {
1235 ReconfigureDatatypeManager();
1240 bool ProfileSyncService::sync_initialized() const {
1241 return backend_initialized_;
1244 bool ProfileSyncService::waiting_for_auth() const {
1245 return is_auth_in_progress_;
1248 bool ProfileSyncService::HasUnrecoverableError() const {
1249 return unrecoverable_error_reason_ != ERROR_REASON_UNSET;
1252 bool ProfileSyncService::IsPassphraseRequired() const {
1253 return passphrase_required_reason_ !=
1254 syncer::REASON_PASSPHRASE_NOT_REQUIRED;
1257 // TODO(zea): Rename this IsPassphraseNeededFromUI and ensure it's used
1258 // appropriately (see http://crbug.com/91379).
1259 bool ProfileSyncService::IsPassphraseRequiredForDecryption() const {
1260 // If there is an encrypted datatype enabled and we don't have the proper
1261 // passphrase, we must prompt the user for a passphrase. The only way for the
1262 // user to avoid entering their passphrase is to disable the encrypted types.
1263 return IsEncryptedDatatypeEnabled() && IsPassphraseRequired();
1266 string16 ProfileSyncService::GetLastSyncedTimeString() const {
1267 if (last_synced_time_.is_null())
1268 return l10n_util::GetStringUTF16(IDS_SYNC_TIME_NEVER);
1270 base::TimeDelta last_synced = base::Time::Now() - last_synced_time_;
1272 if (last_synced < base::TimeDelta::FromMinutes(1))
1273 return l10n_util::GetStringUTF16(IDS_SYNC_TIME_JUST_NOW);
1275 return TimeFormat::TimeElapsed(last_synced);
1278 void ProfileSyncService::UpdateSelectedTypesHistogram(
1279 bool sync_everything, const syncer::ModelTypeSet chosen_types) const {
1280 if (!HasSyncSetupCompleted() ||
1281 sync_everything != sync_prefs_.HasKeepEverythingSynced()) {
1282 UMA_HISTOGRAM_BOOLEAN("Sync.SyncEverything", sync_everything);
1285 // Only log the data types that are shown in the sync settings ui.
1286 const syncer::ModelType model_types[] = {
1287 syncer::APPS,
1288 syncer::AUTOFILL,
1289 syncer::BOOKMARKS,
1290 syncer::EXTENSIONS,
1291 syncer::PASSWORDS,
1292 syncer::PREFERENCES,
1293 syncer::SESSIONS,
1294 syncer::THEMES,
1295 syncer::TYPED_URLS
1298 const browser_sync::user_selectable_type::UserSelectableSyncType
1299 user_selectable_types[] = {
1300 browser_sync::user_selectable_type::APPS,
1301 browser_sync::user_selectable_type::AUTOFILL,
1302 browser_sync::user_selectable_type::BOOKMARKS,
1303 browser_sync::user_selectable_type::EXTENSIONS,
1304 browser_sync::user_selectable_type::PASSWORDS,
1305 browser_sync::user_selectable_type::PREFERENCES,
1306 browser_sync::user_selectable_type::SESSIONS,
1307 browser_sync::user_selectable_type::THEMES,
1308 browser_sync::user_selectable_type::TYPED_URLS
1311 COMPILE_ASSERT(20 == syncer::MODEL_TYPE_COUNT, UpdateCustomConfigHistogram);
1312 COMPILE_ASSERT(arraysize(model_types) ==
1313 browser_sync::user_selectable_type::SELECTABLE_DATATYPE_COUNT,
1314 UpdateCustomConfigHistogram);
1315 COMPILE_ASSERT(arraysize(model_types) == arraysize(user_selectable_types),
1316 UpdateCustomConfigHistogram);
1318 if (!sync_everything) {
1319 const syncer::ModelTypeSet current_types = GetPreferredDataTypes();
1320 for (size_t i = 0; i < arraysize(model_types); ++i) {
1321 const syncer::ModelType type = model_types[i];
1322 if (chosen_types.Has(type) &&
1323 (!HasSyncSetupCompleted() || !current_types.Has(type))) {
1324 // Selected type has changed - log it.
1325 UMA_HISTOGRAM_ENUMERATION(
1326 "Sync.CustomSync",
1327 user_selectable_types[i],
1328 browser_sync::user_selectable_type::SELECTABLE_DATATYPE_COUNT + 1);
1334 #if defined(OS_CHROMEOS)
1335 void ProfileSyncService::RefreshSpareBootstrapToken(
1336 const std::string& passphrase) {
1337 browser_sync::ChromeEncryptor encryptor;
1338 syncer::Cryptographer temp_cryptographer(&encryptor);
1339 // The first 2 params (hostname and username) doesn't have any effect here.
1340 syncer::KeyParams key_params = {"localhost", "dummy", passphrase};
1342 std::string bootstrap_token;
1343 if (!temp_cryptographer.AddKey(key_params)) {
1344 NOTREACHED() << "Failed to add key to cryptographer.";
1346 temp_cryptographer.GetBootstrapToken(&bootstrap_token);
1347 sync_prefs_.SetSpareBootstrapToken(bootstrap_token);
1349 #endif
1351 void ProfileSyncService::OnUserChoseDatatypes(bool sync_everything,
1352 syncer::ModelTypeSet chosen_types) {
1353 if (!backend_.get() && !HasUnrecoverableError()) {
1354 NOTREACHED();
1355 return;
1358 UpdateSelectedTypesHistogram(sync_everything, chosen_types);
1359 sync_prefs_.SetKeepEverythingSynced(sync_everything);
1361 failed_datatypes_handler_.OnUserChoseDatatypes();
1362 ChangePreferredDataTypes(chosen_types);
1363 AcknowledgeSyncedTypes();
1364 NotifyObservers();
1367 void ProfileSyncService::ChangePreferredDataTypes(
1368 syncer::ModelTypeSet preferred_types) {
1370 DVLOG(1) << "ChangePreferredDataTypes invoked";
1371 const syncer::ModelTypeSet registered_types = GetRegisteredDataTypes();
1372 const syncer::ModelTypeSet registered_preferred_types =
1373 Intersection(registered_types, preferred_types);
1374 sync_prefs_.SetPreferredDataTypes(registered_types,
1375 registered_preferred_types);
1377 // Now reconfigure the DTM.
1378 ReconfigureDatatypeManager();
1381 syncer::ModelTypeSet ProfileSyncService::GetPreferredDataTypes() const {
1382 const syncer::ModelTypeSet registered_types = GetRegisteredDataTypes();
1383 const syncer::ModelTypeSet preferred_types =
1384 sync_prefs_.GetPreferredDataTypes(registered_types);
1385 const syncer::ModelTypeSet failed_types =
1386 failed_datatypes_handler_.GetFailedTypes();
1387 return Difference(preferred_types, failed_types);
1390 syncer::ModelTypeSet ProfileSyncService::GetRegisteredDataTypes() const {
1391 syncer::ModelTypeSet registered_types;
1392 // The data_type_controllers_ are determined by command-line flags; that's
1393 // effectively what controls the values returned here.
1394 for (DataTypeController::TypeMap::const_iterator it =
1395 data_type_controllers_.begin();
1396 it != data_type_controllers_.end(); ++it) {
1397 registered_types.Put(it->first);
1399 return registered_types;
1402 bool ProfileSyncService::IsUsingSecondaryPassphrase() const {
1403 syncer::PassphraseType passphrase_type = GetPassphraseType();
1404 return passphrase_type == syncer::FROZEN_IMPLICIT_PASSPHRASE ||
1405 passphrase_type == syncer::CUSTOM_PASSPHRASE;
1408 syncer::PassphraseType ProfileSyncService::GetPassphraseType() const {
1409 return backend_->GetPassphraseType();
1412 base::Time ProfileSyncService::GetExplicitPassphraseTime() const {
1413 return backend_->GetExplicitPassphraseTime();
1416 bool ProfileSyncService::IsCryptographerReady(
1417 const syncer::BaseTransaction* trans) const {
1418 return backend_.get() && backend_->IsCryptographerReady(trans);
1421 SyncBackendHost* ProfileSyncService::GetBackendForTest() {
1422 // We don't check |backend_initialized_|; we assume the test class
1423 // knows what it's doing.
1424 return backend_.get();
1427 void ProfileSyncService::ConfigureDataTypeManager() {
1428 // Don't configure datatypes if the setup UI is still on the screen - this
1429 // is to help multi-screen setting UIs (like iOS) where they don't want to
1430 // start syncing data until the user is done configuring encryption options,
1431 // etc. ReconfigureDatatypeManager() will get called again once the UI calls
1432 // SetSetupInProgress(false).
1433 if (setup_in_progress_)
1434 return;
1436 bool restart = false;
1437 if (!data_type_manager_.get()) {
1438 restart = true;
1439 data_type_manager_.reset(
1440 factory_->CreateDataTypeManager(debug_info_listener_,
1441 backend_.get(),
1442 &data_type_controllers_,
1443 this));
1445 // We create the migrator at the same time.
1446 migrator_.reset(
1447 new browser_sync::BackendMigrator(
1448 profile_->GetDebugName(), GetUserShare(),
1449 this, data_type_manager_.get(),
1450 base::Bind(&ProfileSyncService::StartSyncingWithServer,
1451 base::Unretained(this))));
1454 const syncer::ModelTypeSet types = GetPreferredDataTypes();
1455 if (IsPassphraseRequiredForDecryption()) {
1456 // We need a passphrase still. We don't bother to attempt to configure
1457 // until we receive an OnPassphraseAccepted (which triggers a configure).
1458 DVLOG(1) << "ProfileSyncService::ConfigureDataTypeManager bailing out "
1459 << "because a passphrase required";
1460 NotifyObservers();
1461 return;
1463 syncer::ConfigureReason reason = syncer::CONFIGURE_REASON_UNKNOWN;
1464 if (!HasSyncSetupCompleted()) {
1465 reason = syncer::CONFIGURE_REASON_NEW_CLIENT;
1466 } else if (restart) {
1467 // Datatype downloads on restart are generally due to newly supported
1468 // datatypes (although it's also possible we're picking up where a failed
1469 // previous configuration left off).
1470 // TODO(sync): consider detecting configuration recovery and setting
1471 // the reason here appropriately.
1472 reason = syncer::CONFIGURE_REASON_NEWLY_ENABLED_DATA_TYPE;
1473 } else {
1474 // The user initiated a reconfiguration (either to add or remove types).
1475 reason = syncer::CONFIGURE_REASON_RECONFIGURATION;
1478 data_type_manager_->Configure(types, reason);
1481 syncer::UserShare* ProfileSyncService::GetUserShare() const {
1482 if (backend_.get() && backend_initialized_) {
1483 return backend_->GetUserShare();
1485 NOTREACHED();
1486 return NULL;
1489 syncer::sessions::SyncSessionSnapshot
1490 ProfileSyncService::GetLastSessionSnapshot() const {
1491 if (backend_.get() && backend_initialized_) {
1492 return backend_->GetLastSessionSnapshot();
1494 NOTREACHED();
1495 return syncer::sessions::SyncSessionSnapshot();
1498 bool ProfileSyncService::HasUnsyncedItems() const {
1499 if (backend_.get() && backend_initialized_) {
1500 return backend_->HasUnsyncedItems();
1502 NOTREACHED();
1503 return false;
1506 browser_sync::BackendMigrator*
1507 ProfileSyncService::GetBackendMigratorForTest() {
1508 return migrator_.get();
1511 void ProfileSyncService::GetModelSafeRoutingInfo(
1512 syncer::ModelSafeRoutingInfo* out) const {
1513 if (backend_.get() && backend_initialized_) {
1514 backend_->GetModelSafeRoutingInfo(out);
1515 } else {
1516 NOTREACHED();
1520 Value* ProfileSyncService::GetTypeStatusMap() const {
1521 scoped_ptr<ListValue> result(new ListValue());
1523 if (!backend_.get() || !backend_initialized_) {
1524 return result.release();
1527 std::vector<syncer::SyncError> errors =
1528 failed_datatypes_handler_.GetAllErrors();
1529 std::map<ModelType, syncer::SyncError> error_map;
1530 for (std::vector<syncer::SyncError>::iterator it = errors.begin();
1531 it != errors.end(); ++it) {
1532 error_map[it->type()] = *it;
1535 ModelTypeSet active_types;
1536 ModelTypeSet passive_types;
1537 ModelSafeRoutingInfo routing_info;
1538 backend_->GetModelSafeRoutingInfo(&routing_info);
1539 for (ModelSafeRoutingInfo::const_iterator it = routing_info.begin();
1540 it != routing_info.end(); ++it) {
1541 if (it->second == syncer::GROUP_PASSIVE) {
1542 passive_types.Put(it->first);
1543 } else {
1544 active_types.Put(it->first);
1548 SyncBackendHost::Status detailed_status = backend_->GetDetailedStatus();
1549 ModelTypeSet &throttled_types(detailed_status.throttled_types);
1550 ModelTypeSet registered = GetRegisteredDataTypes();
1551 scoped_ptr<DictionaryValue> type_status_header(new DictionaryValue());
1553 type_status_header->SetString("name", "Model Type");
1554 type_status_header->SetString("status", "header");
1555 type_status_header->SetString("value", "Group Type");
1556 type_status_header->SetString("num_entries", "Total Entries");
1557 type_status_header->SetString("num_live", "Live Entries");
1558 result->Append(type_status_header.release());
1560 scoped_ptr<DictionaryValue> type_status;
1561 for (ModelTypeSet::Iterator it = registered.First(); it.Good(); it.Inc()) {
1562 ModelType type = it.Get();
1564 type_status.reset(new DictionaryValue());
1565 type_status->SetString("name", ModelTypeToString(type));
1567 if (error_map.find(type) != error_map.end()) {
1568 const syncer::SyncError &error = error_map.find(type)->second;
1569 DCHECK(error.IsSet());
1570 std::string error_text = "Error: " + error.location().ToString() +
1571 ", " + error.message();
1572 type_status->SetString("status", "error");
1573 type_status->SetString("value", error_text);
1574 } else if (throttled_types.Has(type) && passive_types.Has(type)) {
1575 type_status->SetString("status", "warning");
1576 type_status->SetString("value", "Passive, Throttled");
1577 } else if (passive_types.Has(type)) {
1578 type_status->SetString("status", "warning");
1579 type_status->SetString("value", "Passive");
1580 } else if (throttled_types.Has(type)) {
1581 type_status->SetString("status", "warning");
1582 type_status->SetString("value", "Throttled");
1583 } else if (active_types.Has(type)) {
1584 type_status->SetString("status", "ok");
1585 type_status->SetString("value", "Active: " +
1586 ModelSafeGroupToString(routing_info[type]));
1587 } else {
1588 type_status->SetString("status", "warning");
1589 type_status->SetString("value", "Disabled by User");
1592 int live_count = detailed_status.num_entries_by_type[type] -
1593 detailed_status.num_to_delete_entries_by_type[type];
1594 type_status->SetInteger("num_entries",
1595 detailed_status.num_entries_by_type[type]);
1596 type_status->SetInteger("num_live", live_count);
1598 result->Append(type_status.release());
1600 return result.release();
1603 void ProfileSyncService::ActivateDataType(
1604 syncer::ModelType type, syncer::ModelSafeGroup group,
1605 ChangeProcessor* change_processor) {
1606 if (!backend_.get()) {
1607 NOTREACHED();
1608 return;
1610 DCHECK(backend_initialized_);
1611 backend_->ActivateDataType(type, group, change_processor);
1614 void ProfileSyncService::DeactivateDataType(syncer::ModelType type) {
1615 if (!backend_.get())
1616 return;
1617 backend_->DeactivateDataType(type);
1620 void ProfileSyncService::ConsumeCachedPassphraseIfPossible() {
1621 // If no cached passphrase, or sync backend hasn't started up yet, just exit.
1622 // If the backend isn't running yet, OnBackendInitialized() will call this
1623 // method again after the backend starts up.
1624 if (cached_passphrase_.empty() || !sync_initialized())
1625 return;
1627 // Backend is up and running, so we can consume the cached passphrase.
1628 std::string passphrase = cached_passphrase_;
1629 cached_passphrase_.clear();
1631 // If we need a passphrase to decrypt data, try the cached passphrase.
1632 if (passphrase_required_reason() == syncer::REASON_DECRYPTION) {
1633 if (SetDecryptionPassphrase(passphrase)) {
1634 DVLOG(1) << "Cached passphrase successfully decrypted pending keys";
1635 return;
1639 // If we get here, we don't have pending keys (or at least, the passphrase
1640 // doesn't decrypt them) - just try to re-encrypt using the encryption
1641 // passphrase.
1642 if (!IsUsingSecondaryPassphrase())
1643 SetEncryptionPassphrase(passphrase, IMPLICIT);
1646 void ProfileSyncService::SetEncryptionPassphrase(const std::string& passphrase,
1647 PassphraseType type) {
1648 // This should only be called when the backend has been initialized.
1649 DCHECK(sync_initialized());
1650 DCHECK(!(type == IMPLICIT && IsUsingSecondaryPassphrase())) <<
1651 "Data is already encrypted using an explicit passphrase";
1652 DCHECK(!(type == EXPLICIT && IsPassphraseRequired())) <<
1653 "Cannot switch to an explicit passphrase if a passphrase is required";
1655 if (type == EXPLICIT)
1656 UMA_HISTOGRAM_BOOLEAN("Sync.CustomPassphrase", true);
1658 DVLOG(1) << "Setting " << (type == EXPLICIT ? "explicit" : "implicit")
1659 << " passphrase for encryption.";
1660 if (passphrase_required_reason_ == syncer::REASON_ENCRYPTION) {
1661 // REASON_ENCRYPTION implies that the cryptographer does not have pending
1662 // keys. Hence, as long as we're not trying to do an invalid passphrase
1663 // change (e.g. explicit -> explicit or explicit -> implicit), we know this
1664 // will succeed. If for some reason a new encryption key arrives via
1665 // sync later, the SBH will trigger another OnPassphraseRequired().
1666 passphrase_required_reason_ = syncer::REASON_PASSPHRASE_NOT_REQUIRED;
1667 NotifyObservers();
1669 backend_->SetEncryptionPassphrase(passphrase, type == EXPLICIT);
1672 bool ProfileSyncService::SetDecryptionPassphrase(
1673 const std::string& passphrase) {
1674 if (IsPassphraseRequired()) {
1675 DVLOG(1) << "Setting passphrase for decryption.";
1676 return backend_->SetDecryptionPassphrase(passphrase);
1677 } else {
1678 NOTREACHED() << "SetDecryptionPassphrase must not be called when "
1679 "IsPassphraseRequired() is false.";
1680 return false;
1684 void ProfileSyncService::EnableEncryptEverything() {
1685 // Tests override sync_initialized() to always return true, so we
1686 // must check that instead of |backend_initialized_|.
1687 // TODO(akalin): Fix the above. :/
1688 DCHECK(sync_initialized());
1689 // TODO(atwilson): Persist the encryption_pending_ flag to address the various
1690 // problems around cancelling encryption in the background (crbug.com/119649).
1691 if (!encrypt_everything_)
1692 encryption_pending_ = true;
1693 UMA_HISTOGRAM_BOOLEAN("Sync.EncryptAllData", true);
1696 bool ProfileSyncService::encryption_pending() const {
1697 // We may be called during the setup process before we're
1698 // initialized (via IsEncryptedDatatypeEnabled and
1699 // IsPassphraseRequiredForDecryption).
1700 return encryption_pending_;
1703 bool ProfileSyncService::EncryptEverythingEnabled() const {
1704 DCHECK(backend_initialized_);
1705 return encrypt_everything_ || encryption_pending_;
1708 syncer::ModelTypeSet ProfileSyncService::GetEncryptedDataTypes() const {
1709 DCHECK(encrypted_types_.Has(syncer::PASSWORDS));
1710 // We may be called during the setup process before we're
1711 // initialized. In this case, we default to the sensitive types.
1712 return encrypted_types_;
1715 void ProfileSyncService::OnSyncManagedPrefChange(bool is_sync_managed) {
1716 NotifyObservers();
1717 if (is_sync_managed) {
1718 DisableForUser();
1719 } else if (HasSyncSetupCompleted() &&
1720 IsSyncEnabledAndLoggedIn() &&
1721 IsSyncTokenAvailable()) {
1722 // Previously-configured sync has been re-enabled, so start sync now.
1723 StartUp();
1727 void ProfileSyncService::Observe(int type,
1728 const content::NotificationSource& source,
1729 const content::NotificationDetails& details) {
1730 switch (type) {
1731 case chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL: {
1732 const GoogleServiceSigninSuccessDetails* successful =
1733 content::Details<const GoogleServiceSigninSuccessDetails>(
1734 details).ptr();
1735 DCHECK(!successful->password.empty());
1736 if (!sync_prefs_.IsStartSuppressed()) {
1737 cached_passphrase_ = successful->password;
1738 // Try to consume the passphrase we just cached. If the sync backend
1739 // is not running yet, the passphrase will remain cached until the
1740 // backend starts up.
1741 ConsumeCachedPassphraseIfPossible();
1743 #if defined(OS_CHROMEOS)
1744 RefreshSpareBootstrapToken(successful->password);
1745 #endif
1746 if (!sync_initialized() ||
1747 GetAuthError().state() != AuthError::NONE) {
1748 // Track the fact that we're still waiting for auth to complete.
1749 is_auth_in_progress_ = true;
1751 break;
1753 case chrome::NOTIFICATION_TOKEN_REQUEST_FAILED: {
1754 const TokenService::TokenRequestFailedDetails& token_details =
1755 *(content::Details<const TokenService::TokenRequestFailedDetails>(
1756 details).ptr());
1757 if (IsTokenServiceRelevant(token_details.service()) &&
1758 !IsSyncTokenAvailable()) {
1759 // The additional check around IsSyncTokenAvailable() above prevents us
1760 // sounding the alarm if we actually have a valid token but a refresh
1761 // attempt by TokenService failed for any variety of reasons (e.g. flaky
1762 // network). It's possible the token we do have is also invalid, but in
1763 // that case we should already have (or can expect) an auth error sent
1764 // from the sync backend.
1765 AuthError error(AuthError::INVALID_GAIA_CREDENTIALS);
1766 UpdateAuthErrorState(error);
1768 break;
1770 case chrome::NOTIFICATION_TOKEN_AVAILABLE: {
1771 const TokenService::TokenAvailableDetails& token_details =
1772 *(content::Details<const TokenService::TokenAvailableDetails>(
1773 details).ptr());
1774 if (IsTokenServiceRelevant(token_details.service()) &&
1775 IsSyncEnabledAndLoggedIn() &&
1776 IsSyncTokenAvailable()) {
1777 if (backend_initialized_)
1778 backend_->UpdateCredentials(GetCredentials());
1779 else
1780 StartUp();
1782 break;
1784 case chrome::NOTIFICATION_TOKEN_LOADING_FINISHED: {
1785 // This notification gets fired when TokenService loads the tokens
1786 // from storage.
1787 if (IsSyncEnabledAndLoggedIn()) {
1788 // Don't start up sync and generate an auth error on auto_start
1789 // platforms as they have their own way to resolve TokenService errors.
1790 // (crbug.com/128592).
1791 if (auto_start_enabled_ && !IsSyncTokenAvailable())
1792 break;
1794 // Initialize the backend if sync is enabled. If the sync token was
1795 // not loaded, GetCredentials() will generate invalid credentials to
1796 // cause the backend to generate an auth error (crbug.com/121755).
1797 if (backend_initialized_)
1798 backend_->UpdateCredentials(GetCredentials());
1799 else
1800 StartUp();
1802 break;
1804 default: {
1805 NOTREACHED();
1810 void ProfileSyncService::AddObserver(Observer* observer) {
1811 observers_.AddObserver(observer);
1814 void ProfileSyncService::RemoveObserver(Observer* observer) {
1815 observers_.RemoveObserver(observer);
1818 bool ProfileSyncService::HasObserver(Observer* observer) const {
1819 return observers_.HasObserver(observer);
1822 base::WeakPtr<syncer::JsController> ProfileSyncService::GetJsController() {
1823 return sync_js_controller_.AsWeakPtr();
1826 void ProfileSyncService::SyncEvent(SyncEventCodes code) {
1827 UMA_HISTOGRAM_ENUMERATION("Sync.EventCodes", code, MAX_SYNC_EVENT_CODE);
1830 // static
1831 bool ProfileSyncService::IsSyncEnabled() {
1832 // We have switches::kEnableSync just in case we need to change back to
1833 // sync-disabled-by-default on a platform.
1834 return !CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableSync);
1837 bool ProfileSyncService::IsManaged() const {
1838 return sync_prefs_.IsManaged();
1841 bool ProfileSyncService::ShouldPushChanges() {
1842 // True only after all bootstrapping has succeeded: the sync backend
1843 // is initialized, all enabled data types are consistent with one
1844 // another, and no unrecoverable error has transpired.
1845 if (HasUnrecoverableError())
1846 return false;
1848 if (!data_type_manager_.get())
1849 return false;
1851 return data_type_manager_->state() == DataTypeManager::CONFIGURED;
1854 void ProfileSyncService::StopAndSuppress() {
1855 sync_prefs_.SetStartSuppressed(true);
1856 ShutdownImpl(false);
1859 void ProfileSyncService::UnsuppressAndStart() {
1860 DCHECK(profile_);
1861 sync_prefs_.SetStartSuppressed(false);
1862 // Set username in SigninManager, as SigninManager::OnGetUserInfoSuccess
1863 // is never called for some clients.
1864 if (signin_ && signin_->GetAuthenticatedUsername().empty()) {
1865 signin_->SetAuthenticatedUsername(sync_prefs_.GetGoogleServicesUsername());
1867 TryStart();
1870 void ProfileSyncService::AcknowledgeSyncedTypes() {
1871 sync_prefs_.AcknowledgeSyncedTypes(GetRegisteredDataTypes());
1874 void ProfileSyncService::ReconfigureDatatypeManager() {
1875 // If we haven't initialized yet, don't configure the DTM as it could cause
1876 // association to start before a Directory has even been created.
1877 if (backend_initialized_) {
1878 DCHECK(backend_.get());
1879 ConfigureDataTypeManager();
1880 } else if (HasUnrecoverableError()) {
1881 // There is nothing more to configure. So inform the listeners,
1882 NotifyObservers();
1884 DVLOG(1) << "ConfigureDataTypeManager not invoked because of an "
1885 << "Unrecoverable error.";
1886 } else {
1887 DVLOG(0) << "ConfigureDataTypeManager not invoked because backend is not "
1888 << "initialized";
1892 const FailedDatatypesHandler& ProfileSyncService::failed_datatypes_handler()
1893 const {
1894 return failed_datatypes_handler_;
1897 void ProfileSyncService::OnInternalUnrecoverableError(
1898 const tracked_objects::Location& from_here,
1899 const std::string& message,
1900 bool delete_sync_database,
1901 UnrecoverableErrorReason reason) {
1902 DCHECK(!HasUnrecoverableError());
1903 unrecoverable_error_reason_ = reason;
1904 OnUnrecoverableErrorImpl(from_here, message, delete_sync_database);
1907 void ProfileSyncService::UpdateInvalidatorRegistrarState() {
1908 const syncer::InvalidatorState effective_state =
1909 backend_initialized_ ?
1910 invalidator_state_ : syncer::TRANSIENT_INVALIDATION_ERROR;
1911 invalidator_registrar_->UpdateInvalidatorState(effective_state);
1914 void ProfileSyncService::ResetForTest() {
1915 Profile* profile = profile_;
1916 SigninManager* signin = SigninManagerFactory::GetForProfile(profile);
1917 ProfileSyncService::StartBehavior behavior =
1918 browser_defaults::kSyncAutoStarts ? ProfileSyncService::AUTO_START
1919 : ProfileSyncService::MANUAL_START;
1921 // We call the destructor and placement new here because we want to explicitly
1922 // recreate a new ProfileSyncService instance at the same memory location as
1923 // the old one. Doing so is fine because this code is run only from within
1924 // integration tests, and the message loop is not running at this point.
1925 // See http://stackoverflow.com/questions/6224121/is-new-this-myclass-undefined-behaviour-after-directly-calling-the-destru.
1926 ProfileSyncService* old_this = this;
1927 this->~ProfileSyncService();
1928 new(old_this) ProfileSyncService(
1929 new ProfileSyncComponentsFactoryImpl(profile,
1930 CommandLine::ForCurrentProcess()),
1931 profile,
1932 signin,
1933 behavior);