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(®istered_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(®istered_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