profile_sync_service.cc revision 3345a6884c488ff3a535c2c9acdd33d74b37e311
1// Copyright (c) 2010 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 <map>
8#include <set>
9
10#include "app/l10n_util.h"
11#include "base/callback.h"
12#include "base/command_line.h"
13#include "base/histogram.h"
14#include "base/logging.h"
15#include "base/stl_util-inl.h"
16#include "base/string16.h"
17#include "base/string_util.h"
18#include "base/task.h"
19#include "base/utf_string_conversions.h"
20#include "chrome/browser/chrome_thread.h"
21#include "chrome/browser/history/history_types.h"
22#include "chrome/browser/platform_util.h"
23#include "chrome/browser/prefs/pref_service.h"
24#include "chrome/browser/profile.h"
25#include "chrome/browser/net/gaia/token_service.h"
26#include "chrome/browser/sync/engine/syncapi.h"
27#include "chrome/browser/sync/glue/change_processor.h"
28#include "chrome/browser/sync/glue/data_type_controller.h"
29#include "chrome/browser/sync/glue/data_type_manager.h"
30#include "chrome/browser/sync/glue/session_data_type_controller.h"
31#include "chrome/browser/sync/profile_sync_factory.h"
32#include "chrome/browser/sync/signin_manager.h"
33#include "chrome/browser/sync/syncable/directory_manager.h"
34#include "chrome/browser/sync/token_migrator.h"
35#include "chrome/browser/sync/util/user_settings.h"
36#include "chrome/common/chrome_switches.h"
37#include "chrome/common/net/gaia/gaia_constants.h"
38#include "chrome/common/notification_details.h"
39#include "chrome/common/notification_service.h"
40#include "chrome/common/notification_source.h"
41#include "chrome/common/notification_type.h"
42#include "chrome/common/pref_names.h"
43#include "chrome/common/time_format.h"
44#include "grit/generated_resources.h"
45#include "jingle/notifier/communicator/const_communicator.h"
46#include "net/base/cookie_monster.h"
47
48using browser_sync::ChangeProcessor;
49using browser_sync::DataTypeController;
50using browser_sync::DataTypeManager;
51using browser_sync::SyncBackendHost;
52using sync_api::SyncCredentials;
53
54typedef GoogleServiceAuthError AuthError;
55
56const char* ProfileSyncService::kSyncServerUrl =
57    "https://clients4.google.com/chrome-sync";
58
59const char* ProfileSyncService::kDevServerUrl =
60    "https://clients4.google.com/chrome-sync/dev";
61
62ProfileSyncService::ProfileSyncService(ProfileSyncFactory* factory,
63                                       Profile* profile,
64                                       const std::string& cros_user)
65    : last_auth_error_(AuthError::None()),
66      factory_(factory),
67      profile_(profile),
68      cros_user_(cros_user),
69      sync_service_url_(kDevServerUrl),
70      backend_initialized_(false),
71      is_auth_in_progress_(false),
72      ALLOW_THIS_IN_INITIALIZER_LIST(wizard_(this)),
73      unrecoverable_error_detected_(false),
74      ALLOW_THIS_IN_INITIALIZER_LIST(scoped_runnable_method_factory_(this)),
75      token_migrator_(NULL),
76      clear_server_data_state_(CLEAR_NOT_STARTED) {
77  DCHECK(factory);
78  DCHECK(profile);
79  registrar_.Add(this,
80                 NotificationType::SYNC_DATA_TYPES_UPDATED,
81                 Source<Profile>(profile));
82
83  // By default, dev & chromium users will go to the development servers.
84  // Dev servers have more features than standard sync servers.
85  // Chrome stable and beta builds will go to the standard sync servers.
86#if defined(GOOGLE_CHROME_BUILD)
87  // For stable, this is "". For dev, this is "dev". For beta, this is "beta".
88  // For daily, this is "canary build".
89  // For linux Chromium builds, this could be anything depending on the
90  // distribution, so always direct those users to dev server urls.
91  // If this is an official build, it will always be one of the above.
92  std::string channel = platform_util::GetVersionStringModifier();
93  if (channel.empty() || channel == "beta") {
94    sync_service_url_ = GURL(kSyncServerUrl);
95  }
96#endif
97}
98
99ProfileSyncService::ProfileSyncService()
100    : last_auth_error_(AuthError::None()),
101      factory_(NULL),
102      profile_(NULL),
103      sync_service_url_(kSyncServerUrl),
104      backend_initialized_(false),
105      is_auth_in_progress_(false),
106      ALLOW_THIS_IN_INITIALIZER_LIST(wizard_(this)),
107      unrecoverable_error_detected_(false),
108      ALLOW_THIS_IN_INITIALIZER_LIST(scoped_runnable_method_factory_(this)),
109      expect_sync_configuration_aborted_(false) {
110}
111
112ProfileSyncService::~ProfileSyncService() {
113  Shutdown(false);
114}
115
116bool ProfileSyncService::AreCredentialsAvailable() {
117  if (IsManaged()) {
118    return false;
119  }
120
121  if (profile_->GetPrefs()->GetBoolean(prefs::kSyncSuppressStart)) {
122    return false;
123  }
124
125  // CrOS user is always logged in. Chrome uses signin_ to check logged in.
126  if (!cros_user_.empty() || !signin_.GetUsername().empty()) {
127    // TODO(chron): Verify CrOS unit test behavior.
128    if (profile()->GetTokenService() &&
129        profile()->GetTokenService()->HasTokenForService(
130            GaiaConstants::kSyncService)) {
131      return true;
132    }
133  }
134  return false;
135}
136
137void ProfileSyncService::LoadMigratedCredentials(const std::string& username,
138                                                 const std::string& token) {
139  signin_.SetUsername(username);
140  profile()->GetPrefs()->SetString(prefs::kGoogleServicesUsername, username);
141  profile()->GetTokenService()->OnIssueAuthTokenSuccess(
142      GaiaConstants::kSyncService, token);
143  profile()->GetPrefs()->SetBoolean(prefs::kSyncCredentialsMigrated, true);
144  token_migrator_.reset();
145}
146
147void ProfileSyncService::Initialize() {
148  InitSettings();
149  RegisterPreferences();
150
151  // Watch the preference that indicates sync is managed so we can take
152  // appropriate action.
153  pref_sync_managed_.Init(prefs::kSyncManaged, profile_->GetPrefs(), this);
154
155  // For now, the only thing we can do through policy is to turn sync off.
156  if (IsManaged()) {
157    DisableForUser();
158    return;
159  }
160
161  RegisterAuthNotifications();
162
163  // In Chrome, we integrate a SigninManager which works with the sync
164  // setup wizard to kick off the TokenService. CrOS does its own plumbing
165  // for the TokenService.
166  if (cros_user_.empty()) {
167    // Will load tokens from DB and broadcast Token events after.
168    signin_.Initialize(profile_);
169  }
170
171  if (!HasSyncSetupCompleted()) {
172    DisableForUser();  // Clean up in case of previous crash / setup abort.
173    if (!cros_user_.empty() && AreCredentialsAvailable()) {
174      StartUp();  // Under ChromeOS, just autostart it anyway if creds are here.
175    }
176  } else if (AreCredentialsAvailable()) {
177    // If we have credentials and sync setup finished, autostart the backend.
178    // Note that if we haven't finished setting up sync, backend bring up will
179    // be done by the wizard.
180    StartUp();
181  } else {
182    // Try to migrate the tokens (if that hasn't already succeeded).
183    if (!profile()->GetPrefs()->GetBoolean(prefs::kSyncCredentialsMigrated)) {
184      token_migrator_.reset(new TokenMigrator(this, profile_->GetPath()));
185      token_migrator_->TryMigration();
186    }
187  }
188
189}
190
191void ProfileSyncService::RegisterAuthNotifications() {
192  registrar_.Add(this,
193                 NotificationType::TOKEN_AVAILABLE,
194                 Source<TokenService>(profile_->GetTokenService()));
195  registrar_.Add(this,
196                 NotificationType::TOKEN_LOADING_FINISHED,
197                 Source<TokenService>(profile_->GetTokenService()));
198  registrar_.Add(this,
199                 NotificationType::GOOGLE_SIGNIN_SUCCESSFUL,
200                 Source<SigninManager>(&signin_));
201  registrar_.Add(this,
202                 NotificationType::GOOGLE_SIGNIN_FAILED,
203                 Source<SigninManager>(&signin_));
204}
205
206void ProfileSyncService::RegisterDataTypeController(
207    DataTypeController* data_type_controller) {
208  DCHECK_EQ(data_type_controllers_.count(data_type_controller->type()), 0U);
209  data_type_controllers_[data_type_controller->type()] =
210      data_type_controller;
211}
212
213browser_sync::SessionModelAssociator*
214    ProfileSyncService::GetSessionModelAssociator() {
215  if (data_type_controllers_.find(syncable::SESSIONS) ==
216      data_type_controllers_.end() ||
217      data_type_controllers_.find(syncable::SESSIONS)->second->state() !=
218      DataTypeController::RUNNING) {
219    return NULL;
220  }
221  return static_cast<browser_sync::SessionDataTypeController*>(
222      data_type_controllers_.find(
223      syncable::SESSIONS)->second.get())->GetModelAssociator();
224}
225
226void ProfileSyncService::ResetClearServerDataState() {
227  clear_server_data_state_ = CLEAR_NOT_STARTED;
228}
229
230ProfileSyncService::ClearServerDataState
231    ProfileSyncService::GetClearServerDataState() {
232  return clear_server_data_state_;
233}
234
235void ProfileSyncService::GetDataTypeControllerStates(
236  browser_sync::DataTypeController::StateMap* state_map) const {
237    browser_sync::DataTypeController::TypeMap::const_iterator iter
238        = data_type_controllers_.begin();
239    for ( ; iter != data_type_controllers_.end(); ++iter ) {
240      (*state_map)[iter->first] = iter->second.get()->state();
241    }
242}
243
244void ProfileSyncService::InitSettings() {
245  const CommandLine& command_line = *CommandLine::ForCurrentProcess();
246
247  // Override the sync server URL from the command-line, if sync server
248  // command-line argument exists.
249  if (command_line.HasSwitch(switches::kSyncServiceURL)) {
250    std::string value(command_line.GetSwitchValueASCII(
251        switches::kSyncServiceURL));
252    if (!value.empty()) {
253      GURL custom_sync_url(value);
254      if (custom_sync_url.is_valid()) {
255        sync_service_url_ = custom_sync_url;
256      } else {
257        LOG(WARNING) << "The following sync URL specified at the command-line "
258                     << "is invalid: " << value;
259      }
260    }
261  }
262
263  // Override the notification server host from the command-line, if provided.
264  if (command_line.HasSwitch(switches::kSyncNotificationHost)) {
265    std::string value(command_line.GetSwitchValueASCII(
266        switches::kSyncNotificationHost));
267    if (!value.empty()) {
268      notifier_options_.xmpp_host_port.set_host(value);
269      notifier_options_.xmpp_host_port.set_port(notifier::kDefaultXmppPort);
270    }
271    LOG(INFO) << "Using " << notifier_options_.xmpp_host_port.ToString()
272        << " for test sync notification server.";
273  }
274
275  notifier_options_.try_ssltcp_first =
276      command_line.HasSwitch(switches::kSyncUseSslTcp);
277  if (notifier_options_.try_ssltcp_first) {
278    LOG(INFO) << "Trying SSL/TCP port before XMPP port for notifications.";
279  }
280
281  if (command_line.HasSwitch(switches::kSyncNotificationMethod)) {
282    const std::string notification_method_str(
283        command_line.GetSwitchValueASCII(switches::kSyncNotificationMethod));
284    notifier_options_.notification_method =
285        notifier::StringToNotificationMethod(notification_method_str);
286  }
287}
288
289void ProfileSyncService::RegisterPreferences() {
290  PrefService* pref_service = profile_->GetPrefs();
291  if (pref_service->FindPreference(prefs::kSyncLastSyncedTime))
292    return;
293  pref_service->RegisterInt64Pref(prefs::kSyncLastSyncedTime, 0);
294  pref_service->RegisterBooleanPref(prefs::kSyncHasSetupCompleted, false);
295  pref_service->RegisterBooleanPref(prefs::kSyncSuppressStart, false);
296  pref_service->RegisterBooleanPref(prefs::kSyncCredentialsMigrated, false);
297
298  // If you've never synced before, or if you're using Chrome OS, all datatypes
299  // are on by default.
300  // TODO(nick): Perhaps a better model would be to always default to false,
301  // and explicitly call SetDataTypes() when the user shows the wizard.
302#if defined(OS_CHROMEOS)
303  bool enable_by_default = true;
304#else
305  bool enable_by_default =
306      !pref_service->HasPrefPath(prefs::kSyncHasSetupCompleted);
307#endif
308
309  pref_service->RegisterBooleanPref(prefs::kSyncBookmarks, true);
310  pref_service->RegisterBooleanPref(prefs::kSyncPasswords, false);
311  pref_service->RegisterBooleanPref(prefs::kSyncPreferences, enable_by_default);
312  pref_service->RegisterBooleanPref(prefs::kSyncAutofill, enable_by_default);
313  pref_service->RegisterBooleanPref(prefs::kSyncThemes, enable_by_default);
314  pref_service->RegisterBooleanPref(prefs::kSyncTypedUrls, enable_by_default);
315  pref_service->RegisterBooleanPref(prefs::kSyncExtensions, enable_by_default);
316  pref_service->RegisterBooleanPref(prefs::kSyncApps, enable_by_default);
317  pref_service->RegisterBooleanPref(prefs::kSyncSessions, enable_by_default);
318  pref_service->RegisterBooleanPref(prefs::kKeepEverythingSynced,
319      enable_by_default);
320  pref_service->RegisterBooleanPref(prefs::kSyncManaged, false);
321  pref_service->RegisterStringPref(prefs::kEncryptionBootstrapToken, "");
322  pref_service->RegisterBooleanPref(prefs::kSyncUsingSecondaryPassphrase,
323      false);
324}
325
326void ProfileSyncService::ClearPreferences() {
327  PrefService* pref_service = profile_->GetPrefs();
328  pref_service->ClearPref(prefs::kSyncLastSyncedTime);
329  pref_service->ClearPref(prefs::kSyncHasSetupCompleted);
330  pref_service->ClearPref(prefs::kEncryptionBootstrapToken);
331  // TODO(nick): The current behavior does not clear e.g. prefs::kSyncBookmarks.
332  // Is that really what we want?
333  pref_service->ScheduleSavePersistentPrefs();
334}
335
336SyncCredentials ProfileSyncService::GetCredentials() {
337  SyncCredentials credentials;
338  credentials.email = !cros_user_.empty() ? cros_user_ : signin_.GetUsername();
339  DCHECK(!credentials.email.empty());
340  TokenService* service = profile_->GetTokenService();
341  credentials.sync_token = service->GetTokenForService(
342      GaiaConstants::kSyncService);
343  return credentials;
344}
345
346void ProfileSyncService::InitializeBackend(bool delete_sync_data_folder) {
347  if (!backend_.get()) {
348    NOTREACHED();
349    return;
350  }
351
352  // TODO(chron): Reimplement invalidate XMPP login / Sync login
353  //              command line switches. Perhaps make it a command
354  //              line in the TokenService itself to pass an arbitrary
355  //              token.
356
357
358  syncable::ModelTypeSet types;
359  // If sync setup hasn't finished, we don't want to initialize routing info
360  // for any data types so that we don't download updates for types that the
361  // user chooses not to sync on the first DownloadUpdatesCommand.
362  if (HasSyncSetupCompleted()) {
363    GetPreferredDataTypes(&types);
364  }
365
366  SyncCredentials credentials = GetCredentials();
367
368  backend_->Initialize(sync_service_url_,
369                       types,
370                       profile_->GetRequestContext(),
371                       credentials,
372                       delete_sync_data_folder,
373                       notifier_options_);
374}
375
376void ProfileSyncService::CreateBackend() {
377  backend_.reset(
378      new SyncBackendHost(this, profile_, profile_->GetPath(),
379                          data_type_controllers_));
380}
381
382void ProfileSyncService::StartUp() {
383  // Don't start up multiple times.
384  if (backend_.get()) {
385    LOG(INFO) << "Skipping bringing up backend host.";
386    return;
387  }
388
389  DCHECK(AreCredentialsAvailable());
390
391  last_synced_time_ = base::Time::FromInternalValue(
392      profile_->GetPrefs()->GetInt64(prefs::kSyncLastSyncedTime));
393
394  CreateBackend();
395
396  registrar_.Add(this,
397                 NotificationType::SYNC_PASSPHRASE_REQUIRED,
398                 Source<SyncBackendHost>(backend_.get()));
399  registrar_.Add(this,
400                 NotificationType::SYNC_PASSPHRASE_ACCEPTED,
401                 Source<SyncBackendHost>(backend_.get()));
402
403  // Initialize the backend.  Every time we start up a new SyncBackendHost,
404  // we'll want to start from a fresh SyncDB, so delete any old one that might
405  // be there.
406  InitializeBackend(!HasSyncSetupCompleted());
407}
408
409void ProfileSyncService::Shutdown(bool sync_disabled) {
410  // Stop all data type controllers, if needed.
411  if (data_type_manager_.get() &&
412      data_type_manager_->state() != DataTypeManager::STOPPED) {
413    data_type_manager_->Stop();
414  }
415
416  data_type_manager_.reset();
417
418  // Move aside the backend so nobody else tries to use it while we are
419  // shutting it down.
420  scoped_ptr<SyncBackendHost> doomed_backend(backend_.release());
421
422  if (doomed_backend.get())
423    doomed_backend->Shutdown(sync_disabled);
424
425  doomed_backend.reset();
426
427
428  // Clear various flags.
429  is_auth_in_progress_ = false;
430  backend_initialized_ = false;
431  last_attempted_user_email_.clear();
432}
433
434void ProfileSyncService::ClearServerData() {
435  clear_server_data_state_ = CLEAR_CLEARING;
436  backend_->RequestClearServerData();
437}
438
439void ProfileSyncService::DisableForUser() {
440  // Clear prefs (including SyncSetupHasCompleted) before shutting down so
441  // PSS clients don't think we're set up while we're shutting down.
442  ClearPreferences();
443  Shutdown(true);
444
445  if (cros_user_.empty()) {
446    signin_.SignOut();
447  }
448
449  FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
450}
451
452bool ProfileSyncService::HasSyncSetupCompleted() const {
453  return profile_->GetPrefs()->GetBoolean(prefs::kSyncHasSetupCompleted);
454}
455
456void ProfileSyncService::SetSyncSetupCompleted() {
457  PrefService* prefs = profile()->GetPrefs();
458  prefs->SetBoolean(prefs::kSyncHasSetupCompleted, true);
459  prefs->SetBoolean(prefs::kSyncSuppressStart, false);
460
461  // Indicate that setup has been completed on the new credentials store
462  // so that we don't try to migrate.
463  prefs->SetBoolean(prefs::kSyncCredentialsMigrated, true);
464
465  prefs->ScheduleSavePersistentPrefs();
466}
467
468void ProfileSyncService::UpdateLastSyncedTime() {
469  last_synced_time_ = base::Time::Now();
470  profile_->GetPrefs()->SetInt64(prefs::kSyncLastSyncedTime,
471      last_synced_time_.ToInternalValue());
472  profile_->GetPrefs()->ScheduleSavePersistentPrefs();
473}
474
475// static
476const char* ProfileSyncService::GetPrefNameForDataType(
477    syncable::ModelType data_type) {
478  switch (data_type) {
479    case syncable::BOOKMARKS:
480      return prefs::kSyncBookmarks;
481    case syncable::PASSWORDS:
482      return prefs::kSyncPasswords;
483    case syncable::PREFERENCES:
484      return prefs::kSyncPreferences;
485    case syncable::AUTOFILL:
486      return prefs::kSyncAutofill;
487    case syncable::THEMES:
488      return prefs::kSyncThemes;
489    case syncable::TYPED_URLS:
490      return prefs::kSyncTypedUrls;
491    case syncable::EXTENSIONS:
492      return prefs::kSyncExtensions;
493    case syncable::APPS:
494      return prefs::kSyncApps;
495    case syncable::SESSIONS:
496      return prefs::kSyncSessions;
497    default:
498      NOTREACHED();
499      return NULL;
500  }
501}
502
503// An invariant has been violated.  Transition to an error state where we try
504// to do as little work as possible, to avoid further corruption or crashes.
505void ProfileSyncService::OnUnrecoverableError(
506    const tracked_objects::Location& from_here,
507    const std::string& message) {
508  unrecoverable_error_detected_ = true;
509  unrecoverable_error_message_ = message;
510  unrecoverable_error_location_.reset(
511      new tracked_objects::Location(from_here.function_name(),
512                                    from_here.file_name(),
513                                    from_here.line_number()));
514
515  // Tell the wizard so it can inform the user only if it is already open.
516  wizard_.Step(SyncSetupWizard::FATAL_ERROR);
517
518  FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
519  LOG(ERROR) << "Unrecoverable error detected -- ProfileSyncService unusable."
520      << message;
521  std::string location;
522  from_here.Write(true, true, &location);
523  LOG(ERROR) << location;
524
525  // Shut all data types down.
526  MessageLoop::current()->PostTask(FROM_HERE,
527        scoped_runnable_method_factory_.NewRunnableMethod(
528        &ProfileSyncService::Shutdown, true));
529}
530
531void ProfileSyncService::OnBackendInitialized() {
532  backend_initialized_ = true;
533
534  // The very first time the backend initializes is effectively the first time
535  // we can say we successfully "synced".  last_synced_time_ will only be null
536  // in this case, because the pref wasn't restored on StartUp.
537  if (last_synced_time_.is_null()) {
538    UpdateLastSyncedTime();
539  }
540  FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
541
542  if (!cros_user_.empty()) {
543    if (profile_->GetPrefs()->GetBoolean(prefs::kSyncSuppressStart)) {
544      ShowChooseDataTypes(NULL);
545    } else {
546      SetSyncSetupCompleted();
547    }
548  }
549
550  if (HasSyncSetupCompleted()) {
551    ConfigureDataTypeManager();
552  }
553}
554
555void ProfileSyncService::OnSyncCycleCompleted() {
556  UpdateLastSyncedTime();
557  FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
558}
559
560void ProfileSyncService::UpdateAuthErrorState(
561    const GoogleServiceAuthError& error) {
562  last_auth_error_ = error;
563  // Protect against the in-your-face dialogs that pop out of nowhere.
564  // Require the user to click somewhere to run the setup wizard in the case
565  // of a steady-state auth failure.
566  if (WizardIsVisible()) {
567    wizard_.Step(AuthError::NONE == last_auth_error_.state() ?
568        SyncSetupWizard::GAIA_SUCCESS : SyncSetupWizard::GAIA_LOGIN);
569  } else {
570    auth_error_time_ = base::TimeTicks::Now();
571  }
572
573  if (!auth_start_time_.is_null()) {
574    UMA_HISTOGRAM_TIMES("Sync.AuthorizationTimeInNetwork",
575                    base::TimeTicks::Now() - auth_start_time_);
576    auth_start_time_ = base::TimeTicks();
577  }
578
579  is_auth_in_progress_ = false;
580  // Fan the notification out to interested UI-thread components.
581  FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
582}
583
584void ProfileSyncService::OnAuthError() {
585  UpdateAuthErrorState(backend_->GetAuthError());
586}
587
588void ProfileSyncService::OnStopSyncingPermanently() {
589  if (SetupInProgress()) {
590    wizard_.Step(SyncSetupWizard::SETUP_ABORTED_BY_PENDING_CLEAR);
591    expect_sync_configuration_aborted_ = true;
592  }
593  profile_->GetPrefs()->SetBoolean(prefs::kSyncSuppressStart, true);
594  DisableForUser();
595}
596
597void ProfileSyncService::OnClearServerDataFailed() {
598  clear_server_data_state_ = CLEAR_FAILED;
599  FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
600}
601
602void ProfileSyncService::OnClearServerDataSucceeded() {
603  clear_server_data_state_ = CLEAR_SUCCEEDED;
604  FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
605}
606
607void ProfileSyncService::ShowLoginDialog(gfx::NativeWindow parent_window) {
608  // TODO(johnnyg): File a bug to make sure this doesn't happen.
609  if (!cros_user_.empty()) {
610    LOG(WARNING) << "ShowLoginDialog called on Chrome OS.";
611    return;
612  }
613
614  if (WizardIsVisible()) {
615    wizard_.Focus();
616    return;
617  }
618
619  if (!auth_error_time_.is_null()) {
620    UMA_HISTOGRAM_LONG_TIMES("Sync.ReauthorizationTime",
621                             base::TimeTicks::Now() - auth_error_time_);
622    auth_error_time_ = base::TimeTicks();  // Reset auth_error_time_ to null.
623  }
624
625  wizard_.SetParent(parent_window);
626  wizard_.Step(SyncSetupWizard::GAIA_LOGIN);
627
628  FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
629}
630
631void ProfileSyncService::ShowChooseDataTypes(gfx::NativeWindow parent_window) {
632  if (WizardIsVisible()) {
633    wizard_.Focus();
634    return;
635  }
636  wizard_.SetParent(parent_window);
637  wizard_.Step(SyncSetupWizard::CHOOSE_DATA_TYPES);
638}
639
640SyncBackendHost::StatusSummary ProfileSyncService::QuerySyncStatusSummary() {
641  if (backend_.get() && backend_initialized_)
642    return backend_->GetStatusSummary();
643  else
644    return SyncBackendHost::Status::OFFLINE_UNUSABLE;
645}
646
647SyncBackendHost::Status ProfileSyncService::QueryDetailedSyncStatus() {
648  if (backend_.get() && backend_initialized_) {
649    return backend_->GetDetailedStatus();
650  } else {
651    SyncBackendHost::Status status =
652        { SyncBackendHost::Status::OFFLINE_UNUSABLE };
653    return status;
654  }
655}
656
657bool ProfileSyncService::SetupInProgress() const {
658  return !HasSyncSetupCompleted() && WizardIsVisible();
659}
660
661std::string ProfileSyncService::BuildSyncStatusSummaryText(
662  const sync_api::SyncManager::Status::Summary& summary) {
663  switch (summary) {
664    case sync_api::SyncManager::Status::OFFLINE:
665      return "OFFLINE";
666    case sync_api::SyncManager::Status::OFFLINE_UNSYNCED:
667      return "OFFLINE_UNSYNCED";
668    case sync_api::SyncManager::Status::SYNCING:
669      return "SYNCING";
670    case sync_api::SyncManager::Status::READY:
671      return "READY";
672    case sync_api::SyncManager::Status::CONFLICT:
673      return "CONFLICT";
674    case sync_api::SyncManager::Status::OFFLINE_UNUSABLE:
675      return "OFFLINE_UNUSABLE";
676    case sync_api::SyncManager::Status::INVALID:  // fall through
677    default:
678      return "UNKNOWN";
679  }
680}
681
682string16 ProfileSyncService::GetLastSyncedTimeString() const {
683  if (last_synced_time_.is_null())
684    return l10n_util::GetStringUTF16(IDS_SYNC_TIME_NEVER);
685
686  base::TimeDelta last_synced = base::Time::Now() - last_synced_time_;
687
688  if (last_synced < base::TimeDelta::FromMinutes(1))
689    return l10n_util::GetStringUTF16(IDS_SYNC_TIME_JUST_NOW);
690
691  return TimeFormat::TimeElapsed(last_synced);
692}
693
694string16 ProfileSyncService::GetAuthenticatedUsername() const {
695  if (backend_.get() && backend_initialized_)
696    return backend_->GetAuthenticatedUsername();
697  else
698    return string16();
699}
700
701void ProfileSyncService::OnUserSubmittedAuth(
702    const std::string& username, const std::string& password,
703    const std::string& captcha) {
704  last_attempted_user_email_ = username;
705  is_auth_in_progress_ = true;
706  FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
707
708  auth_start_time_ = base::TimeTicks::Now();
709
710  // TODO(chron): Mechanism for ChromeOS auth renewal?
711  // (maybe just run the dialog anyway?)
712  // or send it to the CrOS login somehow?
713  if (!cros_user_.empty()) {
714    LOG(WARNING) << "No mechanism on ChromeOS yet. See http://crbug.com/50292";
715  }
716
717  if (!signin_.GetUsername().empty()) {
718    signin_.SignOut();
719  }
720  signin_.StartSignIn(username,
721                      password,
722                      last_auth_error_.captcha().token,
723                      captcha);
724}
725
726void ProfileSyncService::OnUserChoseDatatypes(bool sync_everything,
727    const syncable::ModelTypeSet& chosen_types) {
728  if (!backend_.get()) {
729    NOTREACHED();
730    return;
731  }
732  profile_->GetPrefs()->SetBoolean(prefs::kKeepEverythingSynced,
733      sync_everything);
734
735  ChangePreferredDataTypes(chosen_types);
736  profile_->GetPrefs()->ScheduleSavePersistentPrefs();
737}
738
739void ProfileSyncService::OnUserCancelledDialog() {
740  if (!HasSyncSetupCompleted()) {
741    // A sync dialog was aborted before authentication.
742    // Rollback.
743    expect_sync_configuration_aborted_ = true;
744    DisableForUser();
745  }
746  wizard_.SetParent(NULL);
747
748  // Though an auth could still be in progress, once the dialog is closed we
749  // don't want the UI to stay stuck in the "waiting for authentication" state
750  // as that could take forever.  We set this to false so the buttons to re-
751  // login will appear until either a) the original request finishes and
752  // succeeds, calling OnAuthError(NONE), or b) the user clicks the button,
753  // and tries to re-authenticate. (b) is a little awkward as this second
754  // request will get queued behind the first and could wind up "undoing" the
755  // good if invalid creds were provided, but it's an edge case and the user
756  // can of course get themselves out of it.
757  is_auth_in_progress_ = false;
758  FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
759}
760
761void ProfileSyncService::ChangePreferredDataTypes(
762    const syncable::ModelTypeSet& preferred_types) {
763
764  // Filter out any datatypes which aren't registered, or for which
765  // the preference can't be set.
766  syncable::ModelTypeSet registered_types;
767  GetRegisteredDataTypes(&registered_types);
768  for (int i = 0; i < syncable::MODEL_TYPE_COUNT; ++i) {
769    syncable::ModelType model_type = syncable::ModelTypeFromInt(i);
770    if (!registered_types.count(model_type))
771      continue;
772    const char* pref_name = GetPrefNameForDataType(model_type);
773    if (!pref_name)
774      continue;
775    profile_->GetPrefs()->SetBoolean(pref_name,
776        preferred_types.count(model_type) != 0);
777  }
778
779  // If we haven't initialized yet, don't configure the DTM as it could cause
780  // association to start before a Directory has even been created.
781  if (backend_initialized_)
782    ConfigureDataTypeManager();
783}
784
785void ProfileSyncService::GetPreferredDataTypes(
786    syncable::ModelTypeSet* preferred_types) const {
787  preferred_types->clear();
788
789  // Filter out any datatypes which aren't registered, or for which
790  // the preference can't be read.
791  syncable::ModelTypeSet registered_types;
792  GetRegisteredDataTypes(&registered_types);
793  if (profile_->GetPrefs()->GetBoolean(prefs::kKeepEverythingSynced)) {
794    *preferred_types = registered_types;
795  } else {
796    for (int i = 0; i < syncable::MODEL_TYPE_COUNT; ++i) {
797      syncable::ModelType model_type = syncable::ModelTypeFromInt(i);
798      if (!registered_types.count(model_type))
799        continue;
800      const char* pref_name = GetPrefNameForDataType(model_type);
801      if (!pref_name)
802        continue;
803      if (profile_->GetPrefs()->GetBoolean(pref_name))
804        preferred_types->insert(model_type);
805    }
806  }
807}
808
809void ProfileSyncService::GetRegisteredDataTypes(
810    syncable::ModelTypeSet* registered_types) const {
811  registered_types->clear();
812  // The data_type_controllers_ are determined by command-line flags; that's
813  // effectively what controls the values returned here.
814  for (DataTypeController::TypeMap::const_iterator it =
815       data_type_controllers_.begin();
816       it != data_type_controllers_.end(); ++it) {
817    registered_types->insert((*it).first);
818  }
819}
820
821bool ProfileSyncService::IsCryptographerReady() const {
822  return backend_.get() && backend_initialized_ &&
823      backend_->GetUserShareHandle()->dir_manager->cryptographer()->is_ready();
824}
825
826void ProfileSyncService::SetPassphrase(const std::string& passphrase) {
827  // TODO(tim): This should be encryption-specific instead of passwords
828  // specific.  For now we have to do this to avoid NIGORI node lookups when
829  // we haven't downloaded that node.
830  if (!profile_->GetPrefs()->GetBoolean(prefs::kSyncPasswords)) {
831    LOG(WARNING) << "Silently dropping SetPassphrase request.";
832    return;
833  }
834
835  if (!sync_initialized()) {
836    cached_passphrase_ = passphrase;
837  } else {
838    backend_->SetPassphrase(passphrase);
839  }
840}
841
842void ProfileSyncService::ConfigureDataTypeManager() {
843  if (!data_type_manager_.get()) {
844    data_type_manager_.reset(
845        factory_->CreateDataTypeManager(backend_.get(),
846                                        data_type_controllers_));
847    registrar_.Add(this,
848                   NotificationType::SYNC_CONFIGURE_START,
849                   Source<DataTypeManager>(data_type_manager_.get()));
850    registrar_.Add(this,
851                   NotificationType::SYNC_CONFIGURE_DONE,
852                   Source<DataTypeManager>(data_type_manager_.get()));
853  }
854
855  syncable::ModelTypeSet types;
856  GetPreferredDataTypes(&types);
857  data_type_manager_->Configure(types);
858}
859
860void ProfileSyncService::ActivateDataType(
861    DataTypeController* data_type_controller,
862    ChangeProcessor* change_processor) {
863  if (!backend_.get()) {
864    NOTREACHED();
865    return;
866  }
867  DCHECK(backend_initialized_);
868  change_processor->Start(profile(), backend_->GetUserShareHandle());
869  backend_->ActivateDataType(data_type_controller, change_processor);
870}
871
872void ProfileSyncService::DeactivateDataType(
873    DataTypeController* data_type_controller,
874    ChangeProcessor* change_processor) {
875  change_processor->Stop();
876  if (backend_.get())
877    backend_->DeactivateDataType(data_type_controller, change_processor);
878}
879
880void ProfileSyncService::Observe(NotificationType type,
881                                 const NotificationSource& source,
882                                 const NotificationDetails& details) {
883  switch (type.value) {
884    case NotificationType::SYNC_CONFIGURE_START: {
885      FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
886      // TODO(sync): Maybe toast?
887      break;
888    }
889    case NotificationType::SYNC_CONFIGURE_DONE: {
890      DataTypeManager::ConfigureResult result =
891          *(Details<DataTypeManager::ConfigureResult>(details).ptr());
892      if (result == DataTypeManager::ABORTED &&
893          expect_sync_configuration_aborted_) {
894        expect_sync_configuration_aborted_ = false;
895        return;
896      }
897      if (result != DataTypeManager::OK) {
898        OnUnrecoverableError(FROM_HERE, "Sync Configuration failed.");
899        return;
900      }
901
902      if (!cached_passphrase_.empty()) {
903        // Don't hold on to the passphrase in raw form longer than needed.
904        SetPassphrase(cached_passphrase_);
905        cached_passphrase_.clear();
906      }
907
908      // TODO(sync): Less wizard, more toast.
909      wizard_.Step(SyncSetupWizard::DONE);
910      FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
911
912      break;
913    }
914    case NotificationType::SYNC_PASSPHRASE_REQUIRED: {
915      DCHECK(backend_.get());
916      if (!cached_passphrase_.empty()) {
917        SetPassphrase(cached_passphrase_);
918        cached_passphrase_.clear();
919        break;
920      }
921
922      // TODO(sync): Show the passphrase UI here.
923      UpdateAuthErrorState(GoogleServiceAuthError(
924          GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
925      break;
926    }
927    case NotificationType::SYNC_DATA_TYPES_UPDATED: {
928      if (!HasSyncSetupCompleted()) break;
929
930      syncable::ModelTypeSet types;
931      GetPreferredDataTypes(&types);
932      OnUserChoseDatatypes(false, types);
933      break;
934    }
935    case NotificationType::SYNC_PASSPHRASE_ACCEPTED: {
936      // Make sure the data types that depend on the passphrase are started at
937      // this time.
938      syncable::ModelTypeSet types;
939      GetPreferredDataTypes(&types);
940      data_type_manager_->Configure(types);
941
942      FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
943      break;
944    }
945    case NotificationType::PREF_CHANGED: {
946      std::string* pref_name = Details<std::string>(details).ptr();
947      if (*pref_name == prefs::kSyncManaged) {
948        FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
949        if (*pref_sync_managed_) {
950          DisableForUser();
951        } else if (HasSyncSetupCompleted() && AreCredentialsAvailable()) {
952          StartUp();
953        }
954      }
955      break;
956    }
957    case NotificationType::GOOGLE_SIGNIN_SUCCESSFUL: {
958      if (!profile_->GetPrefs()->GetBoolean(
959          prefs::kSyncUsingSecondaryPassphrase)) {
960        const GoogleServiceSigninSuccessDetails* successful =
961            (Details<const GoogleServiceSigninSuccessDetails>(details).ptr());
962        SetPassphrase(successful->password);
963      }
964      break;
965    }
966    case NotificationType::GOOGLE_SIGNIN_FAILED: {
967      GoogleServiceAuthError error =
968          *(Details<const GoogleServiceAuthError>(details).ptr());
969      UpdateAuthErrorState(error);
970      break;
971    }
972    case NotificationType::TOKEN_AVAILABLE: {
973      if (AreCredentialsAvailable()) {
974        if (backend_initialized_) {
975          backend_->UpdateCredentials(GetCredentials());
976        }
977
978        StartUp();
979      }
980      break;
981    }
982    case NotificationType::TOKEN_LOADING_FINISHED: {
983      // If not in Chrome OS, and we have a username without tokens,
984      // the user will need to signin again, so sign out.
985      if (cros_user_.empty() &&
986          !signin_.GetUsername().empty() &&
987          !AreCredentialsAvailable()) {
988        DisableForUser();
989      }
990      break;
991    }
992    default: {
993      NOTREACHED();
994    }
995  }
996}
997
998void ProfileSyncService::AddObserver(Observer* observer) {
999  observers_.AddObserver(observer);
1000}
1001
1002void ProfileSyncService::RemoveObserver(Observer* observer) {
1003  observers_.RemoveObserver(observer);
1004}
1005
1006void ProfileSyncService::SyncEvent(SyncEventCodes code) {
1007  UMA_HISTOGRAM_ENUMERATION("Sync.EventCodes", code, MAX_SYNC_EVENT_CODE);
1008}
1009
1010// static
1011bool ProfileSyncService::IsSyncEnabled() {
1012  // We have switches::kEnableSync just in case we need to change back to
1013  // sync-disabled-by-default on a platform.
1014  return !CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableSync);
1015}
1016
1017bool ProfileSyncService::IsManaged() {
1018  // Some tests use ProfileSyncServiceMock which doesn't have a profile.
1019  return profile_ && profile_->GetPrefs()->GetBoolean(prefs::kSyncManaged);
1020}
1021
1022bool ProfileSyncService::ShouldPushChanges() {
1023  // True only after all bootstrapping has succeeded: the sync backend
1024  // is initialized, all enabled data types are consistent with one
1025  // another, and no unrecoverable error has transpired.
1026  if (unrecoverable_error_detected_)
1027    return false;
1028
1029  if (!data_type_manager_.get())
1030    return false;
1031
1032  return data_type_manager_->state() == DataTypeManager::CONFIGURED;
1033}
1034