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