profile_sync_service.cc revision ddb351dbec246cf1fab5ec20d2d5520909041de1
1// Copyright (c) 2011 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 <stddef.h>
8#include <map>
9#include <ostream>
10#include <set>
11#include <utility>
12
13#include "base/basictypes.h"
14#include "base/command_line.h"
15#include "base/compiler_specific.h"
16#include "base/logging.h"
17#include "base/memory/ref_counted.h"
18#include "base/message_loop.h"
19#include "base/metrics/histogram.h"
20#include "base/string16.h"
21#include "base/stringprintf.h"
22#include "base/task.h"
23#include "base/threading/thread_restrictions.h"
24#include "chrome/browser/net/gaia/token_service.h"
25#include "chrome/browser/platform_util.h"
26#include "chrome/browser/prefs/pref_service.h"
27#include "chrome/browser/profiles/profile.h"
28#include "chrome/browser/sync/backend_migrator.h"
29#include "chrome/browser/sync/engine/syncapi.h"
30#include "chrome/browser/sync/glue/change_processor.h"
31#include "chrome/browser/sync/glue/data_type_controller.h"
32#include "chrome/browser/sync/glue/data_type_manager.h"
33#include "chrome/browser/sync/glue/session_data_type_controller.h"
34#include "chrome/browser/sync/js_arg_list.h"
35#include "chrome/browser/sync/profile_sync_factory.h"
36#include "chrome/browser/sync/signin_manager.h"
37#include "chrome/browser/ui/browser.h"
38#include "chrome/browser/ui/browser_list.h"
39#include "chrome/common/chrome_switches.h"
40#include "chrome/common/net/gaia/gaia_constants.h"
41#include "chrome/common/pref_names.h"
42#include "chrome/common/time_format.h"
43#include "chrome/common/url_constants.h"
44#include "content/common/notification_details.h"
45#include "content/common/notification_source.h"
46#include "content/common/notification_type.h"
47#include "grit/generated_resources.h"
48#include "ui/base/l10n/l10n_util.h"
49#include "ui/gfx/native_widget_types.h"
50
51using browser_sync::ChangeProcessor;
52using browser_sync::DataTypeController;
53using browser_sync::DataTypeManager;
54using browser_sync::SyncBackendHost;
55using sync_api::SyncCredentials;
56
57typedef GoogleServiceAuthError AuthError;
58
59const char* ProfileSyncService::kSyncServerUrl =
60    "https://clients4.google.com/chrome-sync";
61
62const char* ProfileSyncService::kDevServerUrl =
63    "https://clients4.google.com/chrome-sync/dev";
64
65static const int kSyncClearDataTimeoutInSeconds = 60;  // 1 minute.
66
67ProfileSyncService::ProfileSyncService(ProfileSyncFactory* factory,
68                                       Profile* profile,
69                                       const std::string& cros_user)
70    : last_auth_error_(AuthError::None()),
71      observed_passphrase_required_(false),
72      passphrase_required_for_decryption_(false),
73      passphrase_migration_in_progress_(false),
74      factory_(factory),
75      profile_(profile),
76      cros_user_(cros_user),
77      sync_service_url_(kDevServerUrl),
78      backend_initialized_(false),
79      is_auth_in_progress_(false),
80      wizard_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
81      unrecoverable_error_detected_(false),
82      scoped_runnable_method_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
83      expect_sync_configuration_aborted_(false),
84      clear_server_data_state_(CLEAR_NOT_STARTED) {
85  registrar_.Add(this,
86                 NotificationType::SYNC_DATA_TYPES_UPDATED,
87                 Source<Profile>(profile));
88
89  // By default, dev & chromium users will go to the development servers.
90  // Dev servers have more features than standard sync servers.
91  // Chrome stable and beta builds will go to the standard sync servers.
92#if defined(GOOGLE_CHROME_BUILD)
93  // GetVersionStringModifier hits the registry. See http://crbug.com/70380.
94  base::ThreadRestrictions::ScopedAllowIO allow_io;
95  // For stable, this is "". For dev, this is "dev". For beta, this is "beta".
96  // For daily, this is "canary build".
97  // For Linux Chromium builds, this could be anything depending on the
98  // distribution, so always direct those users to dev server urls.
99  // If this is an official build, it will always be one of the above.
100  std::string channel = platform_util::GetVersionStringModifier();
101  if (channel.empty() || channel == "beta") {
102    sync_service_url_ = GURL(kSyncServerUrl);
103  }
104#endif
105
106  tried_implicit_gaia_remove_when_bug_62103_fixed_ = false;
107}
108
109ProfileSyncService::~ProfileSyncService() {
110  Shutdown(false);
111}
112
113bool ProfileSyncService::AreCredentialsAvailable() {
114  if (IsManaged()) {
115    return false;
116  }
117
118  // CrOS user is always logged in. Chrome uses signin_ to check logged in.
119  if (!cros_user_.empty() || !signin_->GetUsername().empty()) {
120    // TODO(chron): Verify CrOS unit test behavior.
121    if (profile()->GetTokenService() &&
122        profile()->GetTokenService()->HasTokenForService(
123            GaiaConstants::kSyncService)) {
124      return true;
125    }
126  }
127  return false;
128}
129
130void ProfileSyncService::Initialize() {
131  InitSettings();
132  RegisterPreferences();
133
134  // Watch the preference that indicates sync is managed so we can take
135  // appropriate action.
136  pref_sync_managed_.Init(prefs::kSyncManaged, profile_->GetPrefs(), this);
137
138  // For now, the only thing we can do through policy is to turn sync off.
139  if (IsManaged()) {
140    DisableForUser();
141    return;
142  }
143
144  RegisterAuthNotifications();
145
146  // In Chrome, we integrate a SigninManager which works with the sync
147  // setup wizard to kick off the TokenService. CrOS does its own plumbing
148  // for the TokenService.
149  if (cros_user_.empty()) {
150    // Will load tokens from DB and broadcast Token events after.
151    // Note: We rely on signin_ != NULL unless !cros_user_.empty().
152    signin_.reset(new SigninManager());
153    signin_->Initialize(profile_);
154  }
155
156  if (!HasSyncSetupCompleted()) {
157    DisableForUser();  // Clean up in case of previous crash / setup abort.
158
159    // Under ChromeOS, just autostart it anyway if creds are here and start
160    // is not being suppressed by preferences.
161    if (!cros_user_.empty() &&
162        !profile_->GetPrefs()->GetBoolean(prefs::kSyncSuppressStart) &&
163        AreCredentialsAvailable()) {
164      StartUp();
165    }
166  } else if (AreCredentialsAvailable()) {
167    // If we have credentials and sync setup finished, autostart the backend.
168    // Note that if we haven't finished setting up sync, backend bring up will
169    // be done by the wizard.
170    StartUp();
171  }
172}
173
174void ProfileSyncService::RegisterAuthNotifications() {
175  registrar_.Add(this,
176                 NotificationType::TOKEN_AVAILABLE,
177                 Source<TokenService>(profile_->GetTokenService()));
178  registrar_.Add(this,
179                 NotificationType::TOKEN_LOADING_FINISHED,
180                 Source<TokenService>(profile_->GetTokenService()));
181  registrar_.Add(this,
182                 NotificationType::GOOGLE_SIGNIN_SUCCESSFUL,
183                 Source<Profile>(profile_));
184  registrar_.Add(this,
185                 NotificationType::GOOGLE_SIGNIN_FAILED,
186                 Source<Profile>(profile_));
187}
188
189void ProfileSyncService::RegisterDataTypeController(
190    DataTypeController* data_type_controller) {
191  DCHECK_EQ(data_type_controllers_.count(data_type_controller->type()), 0U);
192  data_type_controllers_[data_type_controller->type()] =
193      data_type_controller;
194}
195
196browser_sync::SessionModelAssociator*
197    ProfileSyncService::GetSessionModelAssociator() {
198  if (data_type_controllers_.find(syncable::SESSIONS) ==
199      data_type_controllers_.end() ||
200      data_type_controllers_.find(syncable::SESSIONS)->second->state() !=
201      DataTypeController::RUNNING) {
202    return NULL;
203  }
204  return static_cast<browser_sync::SessionDataTypeController*>(
205      data_type_controllers_.find(
206      syncable::SESSIONS)->second.get())->GetModelAssociator();
207}
208
209void ProfileSyncService::ResetClearServerDataState() {
210  clear_server_data_state_ = CLEAR_NOT_STARTED;
211}
212
213ProfileSyncService::ClearServerDataState
214    ProfileSyncService::GetClearServerDataState() {
215  return clear_server_data_state_;
216}
217
218void ProfileSyncService::GetDataTypeControllerStates(
219  browser_sync::DataTypeController::StateMap* state_map) const {
220    for (browser_sync::DataTypeController::TypeMap::const_iterator iter =
221         data_type_controllers_.begin(); iter != data_type_controllers_.end();
222         ++iter)
223      (*state_map)[iter->first] = iter->second.get()->state();
224}
225
226void ProfileSyncService::InitSettings() {
227  const CommandLine& command_line = *CommandLine::ForCurrentProcess();
228
229  // Override the sync server URL from the command-line, if sync server
230  // command-line argument exists.
231  if (command_line.HasSwitch(switches::kSyncServiceURL)) {
232    std::string value(command_line.GetSwitchValueASCII(
233        switches::kSyncServiceURL));
234    if (!value.empty()) {
235      GURL custom_sync_url(value);
236      if (custom_sync_url.is_valid()) {
237        sync_service_url_ = custom_sync_url;
238      } else {
239        LOG(WARNING) << "The following sync URL specified at the command-line "
240                     << "is invalid: " << value;
241      }
242    }
243  }
244}
245
246void ProfileSyncService::RegisterPreferences() {
247  PrefService* pref_service = profile_->GetPrefs();
248  if (pref_service->FindPreference(prefs::kSyncLastSyncedTime))
249    return;
250  pref_service->RegisterInt64Pref(prefs::kSyncLastSyncedTime, 0);
251  pref_service->RegisterBooleanPref(prefs::kSyncHasSetupCompleted, false);
252  pref_service->RegisterBooleanPref(prefs::kSyncSuppressStart, false);
253
254  // If you've never synced before, or if you're using Chrome OS, all datatypes
255  // are on by default.
256  // TODO(nick): Perhaps a better model would be to always default to false,
257  // and explicitly call SetDataTypes() when the user shows the wizard.
258#if defined(OS_CHROMEOS)
259  bool enable_by_default = true;
260#else
261  bool enable_by_default =
262      !pref_service->HasPrefPath(prefs::kSyncHasSetupCompleted);
263#endif
264
265  pref_service->RegisterBooleanPref(prefs::kSyncBookmarks, true);
266  pref_service->RegisterBooleanPref(prefs::kSyncPasswords, enable_by_default);
267  pref_service->RegisterBooleanPref(prefs::kSyncPreferences, enable_by_default);
268  pref_service->RegisterBooleanPref(prefs::kSyncAutofill, enable_by_default);
269  pref_service->RegisterBooleanPref(prefs::kSyncThemes, enable_by_default);
270  pref_service->RegisterBooleanPref(prefs::kSyncTypedUrls, enable_by_default);
271  pref_service->RegisterBooleanPref(prefs::kSyncExtensions, enable_by_default);
272  pref_service->RegisterBooleanPref(prefs::kSyncApps, enable_by_default);
273  pref_service->RegisterBooleanPref(prefs::kSyncSessions, enable_by_default);
274  pref_service->RegisterBooleanPref(prefs::kKeepEverythingSynced,
275      enable_by_default);
276  pref_service->RegisterBooleanPref(prefs::kSyncManaged, false);
277  pref_service->RegisterStringPref(prefs::kEncryptionBootstrapToken, "");
278
279  pref_service->RegisterBooleanPref(prefs::kSyncAutofillProfile,
280      enable_by_default);
281}
282
283void ProfileSyncService::ClearPreferences() {
284  PrefService* pref_service = profile_->GetPrefs();
285  pref_service->ClearPref(prefs::kSyncLastSyncedTime);
286  pref_service->ClearPref(prefs::kSyncHasSetupCompleted);
287  pref_service->ClearPref(prefs::kEncryptionBootstrapToken);
288
289  // TODO(nick): The current behavior does not clear e.g. prefs::kSyncBookmarks.
290  // Is that really what we want?
291  pref_service->ScheduleSavePersistentPrefs();
292}
293
294SyncCredentials ProfileSyncService::GetCredentials() {
295  SyncCredentials credentials;
296  credentials.email = !cros_user_.empty() ? cros_user_ : signin_->GetUsername();
297  DCHECK(!credentials.email.empty());
298  TokenService* service = profile_->GetTokenService();
299  credentials.sync_token = service->GetTokenForService(
300      GaiaConstants::kSyncService);
301  return credentials;
302}
303
304void ProfileSyncService::InitializeBackend(bool delete_sync_data_folder) {
305  if (!backend_.get()) {
306    NOTREACHED();
307    return;
308  }
309
310  syncable::ModelTypeSet types;
311  // If sync setup hasn't finished, we don't want to initialize routing info
312  // for any data types so that we don't download updates for types that the
313  // user chooses not to sync on the first DownloadUpdatesCommand.
314  if (HasSyncSetupCompleted()) {
315    GetPreferredDataTypes(&types);
316  }
317
318  SyncCredentials credentials = GetCredentials();
319
320  backend_->Initialize(this,
321                       sync_service_url_,
322                       types,
323                       profile_->GetRequestContext(),
324                       credentials,
325                       delete_sync_data_folder);
326}
327
328void ProfileSyncService::CreateBackend() {
329  backend_.reset(new SyncBackendHost(profile_));
330}
331
332bool ProfileSyncService::IsEncryptedDatatypeEnabled() const {
333  return !encrypted_types_.empty();
334}
335
336void ProfileSyncService::StartUp() {
337  // Don't start up multiple times.
338  if (backend_.get()) {
339    VLOG(1) << "Skipping bringing up backend host.";
340    return;
341  }
342
343  DCHECK(AreCredentialsAvailable());
344
345  last_synced_time_ = base::Time::FromInternalValue(
346      profile_->GetPrefs()->GetInt64(prefs::kSyncLastSyncedTime));
347
348  CreateBackend();
349
350  // Initialize the backend.  Every time we start up a new SyncBackendHost,
351  // we'll want to start from a fresh SyncDB, so delete any old one that might
352  // be there.
353  InitializeBackend(!HasSyncSetupCompleted());
354}
355
356void ProfileSyncService::Shutdown(bool sync_disabled) {
357  // Stop all data type controllers, if needed.
358  if (data_type_manager_.get()) {
359    if (data_type_manager_->state() != DataTypeManager::STOPPED) {
360      data_type_manager_->Stop();
361    }
362
363    registrar_.Remove(this,
364                      NotificationType::SYNC_CONFIGURE_START,
365                      Source<DataTypeManager>(data_type_manager_.get()));
366    registrar_.Remove(this,
367                      NotificationType::SYNC_CONFIGURE_DONE,
368                      Source<DataTypeManager>(data_type_manager_.get()));
369    data_type_manager_.reset();
370  }
371
372  js_event_handlers_.RemoveBackend();
373
374  // Move aside the backend so nobody else tries to use it while we are
375  // shutting it down.
376  scoped_ptr<SyncBackendHost> doomed_backend(backend_.release());
377  if (doomed_backend.get()) {
378    doomed_backend->Shutdown(sync_disabled);
379
380    doomed_backend.reset();
381  }
382
383  // Clear various flags.
384  is_auth_in_progress_ = false;
385  backend_initialized_ = false;
386  observed_passphrase_required_ = false;
387  last_attempted_user_email_.clear();
388  last_auth_error_ = GoogleServiceAuthError::None();
389}
390
391void ProfileSyncService::ClearServerData() {
392  clear_server_data_state_ = CLEAR_CLEARING;
393  clear_server_data_timer_.Start(
394      base::TimeDelta::FromSeconds(kSyncClearDataTimeoutInSeconds), this,
395      &ProfileSyncService::OnClearServerDataTimeout);
396  backend_->RequestClearServerData();
397}
398
399void ProfileSyncService::DisableForUser() {
400  // Clear prefs (including SyncSetupHasCompleted) before shutting down so
401  // PSS clients don't think we're set up while we're shutting down.
402  ClearPreferences();
403  Shutdown(true);
404
405  if (signin_.get()) {
406    signin_->SignOut();
407  }
408
409  NotifyObservers();
410}
411
412bool ProfileSyncService::HasSyncSetupCompleted() const {
413  return profile_->GetPrefs()->GetBoolean(prefs::kSyncHasSetupCompleted);
414}
415
416void ProfileSyncService::SetSyncSetupCompleted() {
417  PrefService* prefs = profile()->GetPrefs();
418  prefs->SetBoolean(prefs::kSyncHasSetupCompleted, true);
419  prefs->SetBoolean(prefs::kSyncSuppressStart, false);
420
421  prefs->ScheduleSavePersistentPrefs();
422}
423
424void ProfileSyncService::UpdateLastSyncedTime() {
425  last_synced_time_ = base::Time::Now();
426  profile_->GetPrefs()->SetInt64(prefs::kSyncLastSyncedTime,
427      last_synced_time_.ToInternalValue());
428  profile_->GetPrefs()->ScheduleSavePersistentPrefs();
429}
430
431void ProfileSyncService::NotifyObservers() {
432  FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
433  // TODO(akalin): Make an Observer subclass that listens and does the
434  // event routing.
435  js_event_handlers_.RouteJsEvent(
436      "onSyncServiceStateChanged", browser_sync::JsArgList(), NULL);
437}
438
439// static
440const char* ProfileSyncService::GetPrefNameForDataType(
441    syncable::ModelType data_type) {
442  switch (data_type) {
443    case syncable::BOOKMARKS:
444      return prefs::kSyncBookmarks;
445    case syncable::PASSWORDS:
446      return prefs::kSyncPasswords;
447    case syncable::PREFERENCES:
448      return prefs::kSyncPreferences;
449    case syncable::AUTOFILL:
450      return prefs::kSyncAutofill;
451    case syncable::AUTOFILL_PROFILE:
452      return prefs::kSyncAutofillProfile;
453    case syncable::THEMES:
454      return prefs::kSyncThemes;
455    case syncable::TYPED_URLS:
456      return prefs::kSyncTypedUrls;
457    case syncable::EXTENSIONS:
458      return prefs::kSyncExtensions;
459    case syncable::APPS:
460      return prefs::kSyncApps;
461    case syncable::SESSIONS:
462      return prefs::kSyncSessions;
463    default:
464      break;
465  }
466  NOTREACHED();
467  return NULL;
468}
469
470// An invariant has been violated.  Transition to an error state where we try
471// to do as little work as possible, to avoid further corruption or crashes.
472void ProfileSyncService::OnUnrecoverableError(
473    const tracked_objects::Location& from_here,
474    const std::string& message) {
475  unrecoverable_error_detected_ = true;
476  unrecoverable_error_message_ = message;
477  unrecoverable_error_location_.reset(
478      new tracked_objects::Location(from_here.function_name(),
479                                    from_here.file_name(),
480                                    from_here.line_number()));
481
482  // Tell the wizard so it can inform the user only if it is already open.
483  wizard_.Step(SyncSetupWizard::FATAL_ERROR);
484
485  NotifyObservers();
486  LOG(ERROR) << "Unrecoverable error detected -- ProfileSyncService unusable."
487      << message;
488  std::string location;
489  from_here.Write(true, true, &location);
490  LOG(ERROR) << location;
491
492  // Shut all data types down.
493  MessageLoop::current()->PostTask(FROM_HERE,
494        scoped_runnable_method_factory_.NewRunnableMethod(
495        &ProfileSyncService::Shutdown, true));
496}
497
498void ProfileSyncService::OnBackendInitialized() {
499  backend_initialized_ = true;
500
501  js_event_handlers_.SetBackend(backend_->GetJsBackend());
502
503  // The very first time the backend initializes is effectively the first time
504  // we can say we successfully "synced".  last_synced_time_ will only be null
505  // in this case, because the pref wasn't restored on StartUp.
506  if (last_synced_time_.is_null()) {
507    UpdateLastSyncedTime();
508  }
509  NotifyObservers();
510
511  if (!cros_user_.empty()) {
512    if (profile_->GetPrefs()->GetBoolean(prefs::kSyncSuppressStart)) {
513      ShowConfigure(NULL, true);
514    } else {
515      SetSyncSetupCompleted();
516    }
517  }
518
519  if (HasSyncSetupCompleted()) {
520    ConfigureDataTypeManager();
521  }
522}
523
524void ProfileSyncService::OnSyncCycleCompleted() {
525  UpdateLastSyncedTime();
526  VLOG(2) << "Notifying observers sync cycle completed";
527  NotifyObservers();
528}
529
530void ProfileSyncService::UpdateAuthErrorState(
531    const GoogleServiceAuthError& error) {
532  last_auth_error_ = error;
533  // Protect against the in-your-face dialogs that pop out of nowhere.
534  // Require the user to click somewhere to run the setup wizard in the case
535  // of a steady-state auth failure.
536  if (WizardIsVisible()) {
537    wizard_.Step(AuthError::NONE == last_auth_error_.state() ?
538        SyncSetupWizard::GAIA_SUCCESS : SyncSetupWizard::GAIA_LOGIN);
539  } else {
540    auth_error_time_ = base::TimeTicks::Now();
541  }
542
543  if (!auth_start_time_.is_null()) {
544    UMA_HISTOGRAM_TIMES("Sync.AuthorizationTimeInNetwork",
545                    base::TimeTicks::Now() - auth_start_time_);
546    auth_start_time_ = base::TimeTicks();
547  }
548
549  is_auth_in_progress_ = false;
550  // Fan the notification out to interested UI-thread components.
551  NotifyObservers();
552}
553
554void ProfileSyncService::OnAuthError() {
555  UpdateAuthErrorState(backend_->GetAuthError());
556}
557
558void ProfileSyncService::OnStopSyncingPermanently() {
559  if (SetupInProgress()) {
560    wizard_.Step(SyncSetupWizard::SETUP_ABORTED_BY_PENDING_CLEAR);
561    expect_sync_configuration_aborted_ = true;
562  }
563  profile_->GetPrefs()->SetBoolean(prefs::kSyncSuppressStart, true);
564  DisableForUser();
565}
566
567void ProfileSyncService::OnClearServerDataTimeout() {
568  if (clear_server_data_state_ != CLEAR_SUCCEEDED &&
569      clear_server_data_state_ != CLEAR_FAILED) {
570    clear_server_data_state_ = CLEAR_FAILED;
571    NotifyObservers();
572  }
573}
574
575void ProfileSyncService::OnClearServerDataFailed() {
576  clear_server_data_timer_.Stop();
577
578  // Only once clear has succeeded there is no longer a need to transition to
579  // a failed state as sync is disabled locally.  Also, no need to fire off
580  // the observers if the state didn't change (i.e. it was FAILED before).
581  if (clear_server_data_state_ != CLEAR_SUCCEEDED &&
582      clear_server_data_state_ != CLEAR_FAILED) {
583    clear_server_data_state_ = CLEAR_FAILED;
584    NotifyObservers();
585  }
586}
587
588void ProfileSyncService::OnClearServerDataSucceeded() {
589  clear_server_data_timer_.Stop();
590
591  // Even if the timout fired, we still transition to the succeeded state as
592  // we want UI to update itself and no longer allow the user to press "clear"
593  if (clear_server_data_state_ != CLEAR_SUCCEEDED) {
594    clear_server_data_state_ = CLEAR_SUCCEEDED;
595    NotifyObservers();
596  }
597}
598
599void ProfileSyncService::OnPassphraseRequired(bool for_decryption) {
600  DCHECK(backend_.get());
601  DCHECK(backend_->IsNigoriEnabled());
602
603  // TODO(lipalani) : add this check to other locations as well.
604  if (unrecoverable_error_detected_) {
605    // When unrecoverable error is detected we post a task to shutdown the
606    // backend. The task might not have executed yet.
607    return;
608  }
609  observed_passphrase_required_ = true;
610  passphrase_required_for_decryption_ = for_decryption;
611
612  // First try supplying gaia password as the passphrase.
613  if (!gaia_password_.empty()) {
614    SetPassphrase(gaia_password_, false, true);
615    gaia_password_ = std::string();
616    return;
617  }
618
619  // If the above failed then try the custom passphrase the user might have
620  // entered in setup.
621  if (!cached_passphrase_.value.empty()) {
622    SetPassphrase(cached_passphrase_.value,
623                  cached_passphrase_.is_explicit,
624                  cached_passphrase_.is_creation);
625    cached_passphrase_ = CachedPassphrase();
626    return;
627  }
628
629  // We will skip the passphrase prompt and suppress the warning
630  // if the passphrase is needed for decryption but the user is
631  // not syncing an encrypted data type on this machine.
632  // Otherwise we prompt.
633  if (!IsEncryptedDatatypeEnabled() && for_decryption) {
634    OnPassphraseAccepted();
635    return;
636  }
637
638  if (WizardIsVisible() && for_decryption) {
639    wizard_.Step(SyncSetupWizard::ENTER_PASSPHRASE);
640  }
641
642  NotifyObservers();
643}
644
645void ProfileSyncService::OnPassphraseAccepted() {
646  // Make sure the data types that depend on the passphrase are started at
647  // this time.
648  syncable::ModelTypeSet types;
649  GetPreferredDataTypes(&types);
650  // Reset "passphrase_required" flag before configuring the DataTypeManager
651  // since we know we no longer require the passphrase.
652  observed_passphrase_required_ = false;
653  if (data_type_manager_.get())
654    data_type_manager_->Configure(types);
655
656  NotifyObservers();
657
658  wizard_.Step(SyncSetupWizard::DONE);
659}
660
661void ProfileSyncService::OnEncryptionComplete(
662    const syncable::ModelTypeSet& encrypted_types) {
663  if (encrypted_types_ != encrypted_types) {
664    encrypted_types_ = encrypted_types;
665    NotifyObservers();
666  }
667}
668
669void ProfileSyncService::OnMigrationNeededForTypes(
670    const syncable::ModelTypeSet& types) {
671  DCHECK(backend_initialized_);
672  DCHECK(data_type_manager_.get());
673
674  // Migrator must be valid, because we don't sync until it is created and this
675  // callback originates from a sync cycle.
676  migrator_->MigrateTypes(types);
677}
678
679void ProfileSyncService::ShowLoginDialog(gfx::NativeWindow parent_window) {
680  if (WizardIsVisible()) {
681    wizard_.Focus();
682    // Force the wizard to step to the login screen (which will only actually
683    // happen if the transition is valid).
684    wizard_.Step(SyncSetupWizard::GAIA_LOGIN);
685    return;
686  }
687
688  if (!auth_error_time_.is_null()) {
689    UMA_HISTOGRAM_LONG_TIMES("Sync.ReauthorizationTime",
690                             base::TimeTicks::Now() - auth_error_time_);
691    auth_error_time_ = base::TimeTicks();  // Reset auth_error_time_ to null.
692  }
693
694  wizard_.Step(SyncSetupWizard::GAIA_LOGIN);
695
696  NotifyObservers();
697}
698
699void ProfileSyncService::ShowErrorUI(gfx::NativeWindow parent_window) {
700  if (observed_passphrase_required()) {
701    if (IsUsingSecondaryPassphrase())
702      PromptForExistingPassphrase(parent_window);
703    else
704      SigninForPassphraseMigration(parent_window);
705    return;
706  }
707  const GoogleServiceAuthError& error = GetAuthError();
708  if (error.state() == GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS ||
709      error.state() == GoogleServiceAuthError::CAPTCHA_REQUIRED ||
710      error.state() == GoogleServiceAuthError::ACCOUNT_DELETED ||
711      error.state() == GoogleServiceAuthError::ACCOUNT_DISABLED ||
712      error.state() == GoogleServiceAuthError::SERVICE_UNAVAILABLE) {
713    ShowLoginDialog(parent_window);
714  }
715}
716
717
718void ProfileSyncService::ShowConfigure(
719    gfx::NativeWindow parent_window, bool sync_everything) {
720  if (WizardIsVisible()) {
721    wizard_.Focus();
722    return;
723  }
724
725  if (sync_everything)
726    wizard_.Step(SyncSetupWizard::SYNC_EVERYTHING);
727  else
728    wizard_.Step(SyncSetupWizard::CONFIGURE);
729}
730
731void ProfileSyncService::PromptForExistingPassphrase(
732    gfx::NativeWindow parent_window) {
733  if (WizardIsVisible()) {
734    wizard_.Focus();
735    return;
736  }
737
738  wizard_.Step(SyncSetupWizard::ENTER_PASSPHRASE);
739}
740
741void ProfileSyncService::SigninForPassphraseMigration(
742    gfx::NativeWindow parent_window) {
743  passphrase_migration_in_progress_ = true;
744  ShowLoginDialog(parent_window);
745}
746
747SyncBackendHost::StatusSummary ProfileSyncService::QuerySyncStatusSummary() {
748  if (backend_.get() && backend_initialized_)
749    return backend_->GetStatusSummary();
750  else
751    return SyncBackendHost::Status::OFFLINE_UNUSABLE;
752}
753
754SyncBackendHost::Status ProfileSyncService::QueryDetailedSyncStatus() {
755  if (backend_.get() && backend_initialized_) {
756    return backend_->GetDetailedStatus();
757  } else {
758    SyncBackendHost::Status status =
759        { SyncBackendHost::Status::OFFLINE_UNUSABLE };
760    return status;
761  }
762}
763
764bool ProfileSyncService::SetupInProgress() const {
765  return !HasSyncSetupCompleted() && WizardIsVisible();
766}
767
768std::string ProfileSyncService::BuildSyncStatusSummaryText(
769    const sync_api::SyncManager::Status::Summary& summary) {
770  const char* strings[] = {"INVALID", "OFFLINE", "OFFLINE_UNSYNCED", "SYNCING",
771      "READY", "CONFLICT", "OFFLINE_UNUSABLE"};
772  COMPILE_ASSERT(arraysize(strings) ==
773                 sync_api::SyncManager::Status::SUMMARY_STATUS_COUNT,
774                 enum_indexed_array);
775  if (summary < 0 ||
776      summary >= sync_api::SyncManager::Status::SUMMARY_STATUS_COUNT) {
777    LOG(DFATAL) << "Illegal Summary Value: " << summary;
778    return "UNKNOWN";
779  }
780  return strings[summary];
781}
782
783bool ProfileSyncService::unrecoverable_error_detected() const {
784  return unrecoverable_error_detected_;
785}
786
787string16 ProfileSyncService::GetLastSyncedTimeString() const {
788  if (last_synced_time_.is_null())
789    return l10n_util::GetStringUTF16(IDS_SYNC_TIME_NEVER);
790
791  base::TimeDelta last_synced = base::Time::Now() - last_synced_time_;
792
793  if (last_synced < base::TimeDelta::FromMinutes(1))
794    return l10n_util::GetStringUTF16(IDS_SYNC_TIME_JUST_NOW);
795
796  return TimeFormat::TimeElapsed(last_synced);
797}
798
799string16 ProfileSyncService::GetAuthenticatedUsername() const {
800  if (backend_.get() && backend_initialized_)
801    return backend_->GetAuthenticatedUsername();
802  else
803    return string16();
804}
805
806void ProfileSyncService::OnUserSubmittedAuth(
807    const std::string& username, const std::string& password,
808    const std::string& captcha, const std::string& access_code) {
809  last_attempted_user_email_ = username;
810  is_auth_in_progress_ = true;
811  NotifyObservers();
812
813  auth_start_time_ = base::TimeTicks::Now();
814
815  if (!signin_.get()) {
816    // In ChromeOS we sign in during login, so we do not instantiate signin_.
817    // If this function gets called, we need to re-authenticate (e.g. for
818    // two factor signin), so instantiate signin_ here.
819    signin_.reset(new SigninManager());
820    signin_->Initialize(profile_);
821  }
822
823  if (!access_code.empty()) {
824    signin_->ProvideSecondFactorAccessCode(access_code);
825    return;
826  }
827
828  if (!signin_->GetUsername().empty()) {
829    signin_->SignOut();
830  }
831
832  // The user has submitted credentials, which indicates they don't
833  // want to suppress start up anymore.
834  PrefService* prefs = profile_->GetPrefs();
835  prefs->SetBoolean(prefs::kSyncSuppressStart, false);
836  prefs->ScheduleSavePersistentPrefs();
837
838  signin_->StartSignIn(username,
839                       password,
840                       last_auth_error_.captcha().token,
841                       captcha);
842}
843
844void ProfileSyncService::OnUserChoseDatatypes(bool sync_everything,
845    const syncable::ModelTypeSet& chosen_types) {
846  if (!backend_.get()) {
847    NOTREACHED();
848    return;
849  }
850
851  profile_->GetPrefs()->SetBoolean(prefs::kKeepEverythingSynced,
852      sync_everything);
853
854  ChangePreferredDataTypes(chosen_types);
855  profile_->GetPrefs()->ScheduleSavePersistentPrefs();
856}
857
858void ProfileSyncService::OnUserCancelledDialog() {
859  if (!HasSyncSetupCompleted()) {
860    // A sync dialog was aborted before authentication.
861    // Rollback.
862    expect_sync_configuration_aborted_ = true;
863    DisableForUser();
864  }
865
866  // Though an auth could still be in progress, once the dialog is closed we
867  // don't want the UI to stay stuck in the "waiting for authentication" state
868  // as that could take forever.  We set this to false so the buttons to re-
869  // login will appear until either a) the original request finishes and
870  // succeeds, calling OnAuthError(NONE), or b) the user clicks the button,
871  // and tries to re-authenticate. (b) is a little awkward as this second
872  // request will get queued behind the first and could wind up "undoing" the
873  // good if invalid creds were provided, but it's an edge case and the user
874  // can of course get themselves out of it.
875  is_auth_in_progress_ = false;
876  NotifyObservers();
877}
878
879void ProfileSyncService::ChangePreferredDataTypes(
880    const syncable::ModelTypeSet& preferred_types) {
881
882  // Filter out any datatypes which aren't registered, or for which
883  // the preference can't be set.
884  syncable::ModelTypeSet registered_types;
885  GetRegisteredDataTypes(&registered_types);
886  for (int i = 0; i < syncable::MODEL_TYPE_COUNT; ++i) {
887    syncable::ModelType model_type = syncable::ModelTypeFromInt(i);
888    if (!registered_types.count(model_type))
889      continue;
890    const char* pref_name = GetPrefNameForDataType(model_type);
891    if (!pref_name)
892      continue;
893    profile_->GetPrefs()->SetBoolean(pref_name,
894        preferred_types.count(model_type) != 0);
895    if (syncable::AUTOFILL == model_type) {
896      profile_->GetPrefs()->SetBoolean(prefs::kSyncAutofillProfile,
897          preferred_types.count(model_type) != 0);
898    }
899  }
900
901  // If we haven't initialized yet, don't configure the DTM as it could cause
902  // association to start before a Directory has even been created.
903  if (backend_initialized_)
904    ConfigureDataTypeManager();
905}
906
907void ProfileSyncService::GetPreferredDataTypes(
908    syncable::ModelTypeSet* preferred_types) const {
909  preferred_types->clear();
910  if (profile_->GetPrefs()->GetBoolean(prefs::kKeepEverythingSynced)) {
911    GetRegisteredDataTypes(preferred_types);
912  } else {
913    // Filter out any datatypes which aren't registered, or for which
914    // the preference can't be read.
915    syncable::ModelTypeSet registered_types;
916    GetRegisteredDataTypes(&registered_types);
917    for (int i = 0; i < syncable::MODEL_TYPE_COUNT; ++i) {
918      syncable::ModelType model_type = syncable::ModelTypeFromInt(i);
919      if (!registered_types.count(model_type))
920        continue;
921      if (model_type == syncable::AUTOFILL_PROFILE)
922        continue;
923      const char* pref_name = GetPrefNameForDataType(model_type);
924      if (!pref_name)
925        continue;
926
927      // We are trying to group autofill_profile tag with the same
928      // enabled/disabled state as autofill. Because the UI only shows autofill.
929      if (profile_->GetPrefs()->GetBoolean(pref_name)) {
930        preferred_types->insert(model_type);
931        if (model_type == syncable::AUTOFILL) {
932          if (!registered_types.count(syncable::AUTOFILL_PROFILE))
933            continue;
934          preferred_types->insert(syncable::AUTOFILL_PROFILE);
935        }
936      }
937    }
938  }
939}
940
941void ProfileSyncService::GetRegisteredDataTypes(
942    syncable::ModelTypeSet* registered_types) const {
943  registered_types->clear();
944  // The data_type_controllers_ are determined by command-line flags; that's
945  // effectively what controls the values returned here.
946  for (DataTypeController::TypeMap::const_iterator it =
947       data_type_controllers_.begin();
948       it != data_type_controllers_.end(); ++it) {
949    registered_types->insert((*it).first);
950  }
951}
952
953bool ProfileSyncService::IsUsingSecondaryPassphrase() const {
954  return backend_.get() && (backend_->IsUsingExplicitPassphrase() ||
955      (tried_implicit_gaia_remove_when_bug_62103_fixed_ &&
956       observed_passphrase_required_));
957}
958
959bool ProfileSyncService::IsCryptographerReady(
960    const sync_api::BaseTransaction* trans) const {
961  return backend_.get() && backend_->IsCryptographerReady(trans);
962}
963
964SyncBackendHost* ProfileSyncService::GetBackendForTest() {
965  // We don't check |backend_initialized_|; we assume the test class
966  // knows what it's doing.
967  return backend_.get();
968}
969
970void ProfileSyncService::ConfigureDataTypeManager() {
971  if (!data_type_manager_.get()) {
972    data_type_manager_.reset(
973        factory_->CreateDataTypeManager(backend_.get(),
974                                        data_type_controllers_));
975    registrar_.Add(this,
976                   NotificationType::SYNC_CONFIGURE_START,
977                   Source<DataTypeManager>(data_type_manager_.get()));
978    registrar_.Add(this,
979                   NotificationType::SYNC_CONFIGURE_DONE,
980                   Source<DataTypeManager>(data_type_manager_.get()));
981
982    // We create the migrator at the same time.
983    migrator_.reset(
984        new browser_sync::BackendMigrator(this, data_type_manager_.get()));
985  }
986
987  syncable::ModelTypeSet types;
988  GetPreferredDataTypes(&types);
989  // We set this special case here since it's the only datatype whose encryption
990  // status we already know. All others are set after the initial sync
991  // completes (for now).
992  // TODO(zea): Implement a better way that uses preferences for which types
993  // need encryption.
994  encrypted_types_.clear();
995  if (types.count(syncable::PASSWORDS) > 0)
996    encrypted_types_.insert(syncable::PASSWORDS);
997  if (observed_passphrase_required_ && passphrase_required_for_decryption_) {
998    if (IsEncryptedDatatypeEnabled()) {
999      // We need a passphrase still. Prompt the user for a passphrase, and
1000      // DataTypeManager::Configure() will get called once the passphrase is
1001      // accepted.
1002      OnPassphraseRequired(true);
1003      return;
1004    } else {
1005      // We've been informed that a passphrase is required for decryption, but
1006      // now there are no encrypted data types enabled, so clear the flag
1007      // (NotifyObservers() will be called when configuration completes).
1008      observed_passphrase_required_ = false;
1009    }
1010  }
1011  data_type_manager_->Configure(types);
1012}
1013
1014sync_api::UserShare* ProfileSyncService::GetUserShare() const {
1015  if (backend_.get() && backend_initialized_) {
1016    return backend_->GetUserShare();
1017  }
1018  NOTREACHED();
1019  return NULL;
1020}
1021
1022const browser_sync::sessions::SyncSessionSnapshot*
1023    ProfileSyncService::GetLastSessionSnapshot() const {
1024  if (backend_.get() && backend_initialized_) {
1025    return backend_->GetLastSessionSnapshot();
1026  }
1027  NOTREACHED();
1028  return NULL;
1029}
1030
1031bool ProfileSyncService::HasUnsyncedItems() const {
1032  if (backend_.get() && backend_initialized_) {
1033    return backend_->HasUnsyncedItems();
1034  }
1035  NOTREACHED();
1036  return false;
1037}
1038
1039void ProfileSyncService::GetModelSafeRoutingInfo(
1040    browser_sync::ModelSafeRoutingInfo* out) {
1041  if (backend_.get() && backend_initialized_) {
1042    backend_->GetModelSafeRoutingInfo(out);
1043  } else {
1044    NOTREACHED();
1045  }
1046}
1047
1048syncable::AutofillMigrationState
1049    ProfileSyncService::GetAutofillMigrationState() {
1050  if (backend_.get() && backend_initialized_) {
1051    return backend_->GetAutofillMigrationState();
1052  }
1053  NOTREACHED();
1054  return syncable::NOT_DETERMINED;
1055}
1056
1057void ProfileSyncService::SetAutofillMigrationState(
1058    syncable::AutofillMigrationState state) {
1059  if (backend_.get() && backend_initialized_) {
1060    backend_->SetAutofillMigrationState(state);
1061  } else {
1062    NOTREACHED();
1063  }
1064}
1065
1066syncable::AutofillMigrationDebugInfo
1067    ProfileSyncService::GetAutofillMigrationDebugInfo() {
1068  if (backend_.get() && backend_initialized_) {
1069    return backend_->GetAutofillMigrationDebugInfo();
1070  }
1071  NOTREACHED();
1072  syncable::AutofillMigrationDebugInfo debug_info = { 0 };
1073  return debug_info;
1074}
1075
1076void ProfileSyncService::SetAutofillMigrationDebugInfo(
1077    syncable::AutofillMigrationDebugInfo::PropertyToSet property_to_set,
1078    const syncable::AutofillMigrationDebugInfo& info) {
1079  if (backend_.get() && backend_initialized_) {
1080    backend_->SetAutofillMigrationDebugInfo(property_to_set, info);
1081  } else {
1082    NOTREACHED();
1083  }
1084}
1085
1086void ProfileSyncService::ActivateDataType(
1087    DataTypeController* data_type_controller,
1088    ChangeProcessor* change_processor) {
1089  if (!backend_.get()) {
1090    NOTREACHED();
1091    return;
1092  }
1093  DCHECK(backend_initialized_);
1094  change_processor->Start(profile(), backend_->GetUserShare());
1095  backend_->ActivateDataType(data_type_controller, change_processor);
1096}
1097
1098void ProfileSyncService::DeactivateDataType(
1099    DataTypeController* data_type_controller,
1100    ChangeProcessor* change_processor) {
1101  change_processor->Stop();
1102  if (backend_.get())
1103    backend_->DeactivateDataType(data_type_controller, change_processor);
1104}
1105
1106void ProfileSyncService::SetPassphrase(const std::string& passphrase,
1107                                       bool is_explicit,
1108                                       bool is_creation) {
1109  if (ShouldPushChanges() || observed_passphrase_required_) {
1110    backend_->SetPassphrase(passphrase, is_explicit);
1111  } else {
1112    if (is_explicit) {
1113      cached_passphrase_.value = passphrase;
1114      cached_passphrase_.is_explicit = is_explicit;
1115      cached_passphrase_.is_creation = is_creation;
1116    } else {
1117      gaia_password_ = passphrase;
1118    }
1119  }
1120}
1121
1122void ProfileSyncService::EncryptDataTypes(
1123    const syncable::ModelTypeSet& encrypted_types) {
1124  backend_->EncryptDataTypes(encrypted_types);
1125}
1126
1127void ProfileSyncService::GetEncryptedDataTypes(
1128    syncable::ModelTypeSet* encrypted_types) const {
1129  *encrypted_types = encrypted_types_;
1130}
1131
1132void ProfileSyncService::Observe(NotificationType type,
1133                                 const NotificationSource& source,
1134                                 const NotificationDetails& details) {
1135  switch (type.value) {
1136    case NotificationType::SYNC_CONFIGURE_START: {
1137      NotifyObservers();
1138      // TODO(sync): Maybe toast?
1139      break;
1140    }
1141    case NotificationType::SYNC_CONFIGURE_DONE: {
1142      DataTypeManager::ConfigureResultWithErrorLocation* result_with_location =
1143          Details<DataTypeManager::ConfigureResultWithErrorLocation>(
1144              details).ptr();
1145
1146      DataTypeManager::ConfigureResult result = result_with_location->result;
1147      if (result == DataTypeManager::ABORTED &&
1148          expect_sync_configuration_aborted_) {
1149        expect_sync_configuration_aborted_ = false;
1150        return;
1151      }
1152      // Clear out the gaia password if it is already there.
1153      gaia_password_ = std::string();
1154      if (result != DataTypeManager::OK) {
1155        std::string message = StringPrintf("Sync Configuration failed with %d",
1156                                            result);
1157        OnUnrecoverableError(*(result_with_location->location), message);
1158        cached_passphrase_ = CachedPassphrase();
1159        return;
1160      }
1161
1162      // If the user had entered a custom passphrase use it now.
1163      if (!cached_passphrase_.value.empty()) {
1164        // Don't hold on to the passphrase in raw form longer than needed.
1165        SetPassphrase(cached_passphrase_.value,
1166                      cached_passphrase_.is_explicit,
1167                      cached_passphrase_.is_creation);
1168        cached_passphrase_ = CachedPassphrase();
1169      }
1170
1171      // We should never get in a state where we have no encrypted datatypes
1172      // enabled, and yet we still think we require a passphrase.
1173      DCHECK(!(observed_passphrase_required_ &&
1174               passphrase_required_for_decryption_ &&
1175               !IsEncryptedDatatypeEnabled()));
1176
1177      // TODO(sync): Less wizard, more toast.
1178      wizard_.Step(SyncSetupWizard::DONE);
1179      NotifyObservers();
1180
1181      // In the old world, this would be a no-op.  With new syncer thread,
1182      // this is the point where it is safe to switch from config-mode to
1183      // normal operation.
1184      backend_->StartSyncingWithServer();
1185      break;
1186    }
1187    case NotificationType::SYNC_DATA_TYPES_UPDATED: {
1188      if (!HasSyncSetupCompleted()) break;
1189
1190      syncable::ModelTypeSet types;
1191      GetPreferredDataTypes(&types);
1192      OnUserChoseDatatypes(false, types);
1193      break;
1194    }
1195    case NotificationType::PREF_CHANGED: {
1196      std::string* pref_name = Details<std::string>(details).ptr();
1197      if (*pref_name == prefs::kSyncManaged) {
1198        NotifyObservers();
1199        if (*pref_sync_managed_) {
1200          DisableForUser();
1201        } else if (HasSyncSetupCompleted() && AreCredentialsAvailable()) {
1202          StartUp();
1203        }
1204      }
1205      break;
1206    }
1207    case NotificationType::GOOGLE_SIGNIN_SUCCESSFUL: {
1208      const GoogleServiceSigninSuccessDetails* successful =
1209          (Details<const GoogleServiceSigninSuccessDetails>(details).ptr());
1210      // We pass 'false' to SetPassphrase to denote that this is an implicit
1211      // request and shouldn't override an explicit one.  Thus, we either
1212      // update the implicit passphrase (idempotent if the passphrase didn't
1213      // actually change), or the user has an explicit passphrase set so this
1214      // becomes a no-op.
1215      tried_implicit_gaia_remove_when_bug_62103_fixed_ = true;
1216      SetPassphrase(successful->password, false, true);
1217
1218      // If this signin was to initiate a passphrase migration (on the
1219      // first computer, thus not for decryption), continue the migration.
1220      if (passphrase_migration_in_progress_ &&
1221          !passphrase_required_for_decryption_) {
1222        wizard_.Step(SyncSetupWizard::PASSPHRASE_MIGRATION);
1223        passphrase_migration_in_progress_ = false;
1224      }
1225
1226      break;
1227    }
1228    case NotificationType::GOOGLE_SIGNIN_FAILED: {
1229      GoogleServiceAuthError error =
1230          *(Details<const GoogleServiceAuthError>(details).ptr());
1231      UpdateAuthErrorState(error);
1232      break;
1233    }
1234    case NotificationType::TOKEN_AVAILABLE: {
1235      if (AreCredentialsAvailable()) {
1236        if (backend_initialized_) {
1237          backend_->UpdateCredentials(GetCredentials());
1238        }
1239
1240        if (!profile_->GetPrefs()->GetBoolean(prefs::kSyncSuppressStart))
1241          StartUp();
1242      }
1243      break;
1244    }
1245    case NotificationType::TOKEN_LOADING_FINISHED: {
1246      // If not in Chrome OS, and we have a username without tokens,
1247      // the user will need to signin again, so sign out.
1248      if (cros_user_.empty() &&
1249          !signin_->GetUsername().empty() &&
1250          !AreCredentialsAvailable()) {
1251        DisableForUser();
1252      }
1253      break;
1254    }
1255    default: {
1256      NOTREACHED();
1257    }
1258  }
1259}
1260
1261void ProfileSyncService::AddObserver(Observer* observer) {
1262  observers_.AddObserver(observer);
1263}
1264
1265void ProfileSyncService::RemoveObserver(Observer* observer) {
1266  observers_.RemoveObserver(observer);
1267}
1268
1269bool ProfileSyncService::HasObserver(Observer* observer) const {
1270  return observers_.HasObserver(observer);
1271}
1272
1273browser_sync::JsFrontend* ProfileSyncService::GetJsFrontend() {
1274  return &js_event_handlers_;
1275}
1276
1277void ProfileSyncService::SyncEvent(SyncEventCodes code) {
1278  UMA_HISTOGRAM_ENUMERATION("Sync.EventCodes", code, MAX_SYNC_EVENT_CODE);
1279}
1280
1281// static
1282bool ProfileSyncService::IsSyncEnabled() {
1283  // We have switches::kEnableSync just in case we need to change back to
1284  // sync-disabled-by-default on a platform.
1285  return !CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableSync);
1286}
1287
1288bool ProfileSyncService::IsManaged() {
1289  // Some tests use ProfileSyncServiceMock which doesn't have a profile.
1290  return profile_ && profile_->GetPrefs()->GetBoolean(prefs::kSyncManaged);
1291}
1292
1293bool ProfileSyncService::ShouldPushChanges() {
1294  // True only after all bootstrapping has succeeded: the sync backend
1295  // is initialized, all enabled data types are consistent with one
1296  // another, and no unrecoverable error has transpired.
1297  if (unrecoverable_error_detected_)
1298    return false;
1299
1300  if (!data_type_manager_.get())
1301    return false;
1302
1303  return data_type_manager_->state() == DataTypeManager::CONFIGURED;
1304}
1305