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