blob: 5664118298b9630d40cd70f32d36f0a2aba5babc [file] [log] [blame]
// Copyright 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ios/chrome/browser/sync/sync_setup_service.h"
#include <stdio.h>
#include "base/metrics/histogram_macros.h"
#include "components/sync/base/stop_source.h"
#include "components/sync/base/user_selectable_type.h"
#include "components/sync/driver/sync_service.h"
#include "components/sync/driver/sync_user_settings.h"
#include "google_apis/gaia/google_service_auth_error.h"
namespace {
// The set of user-selectable datatypes. This must be in the same order as
// |SyncSetupService::SyncableDatatype|.
syncer::ModelType kDataTypes[] = {
syncer::BOOKMARKS, syncer::TYPED_URLS, syncer::PASSWORDS,
syncer::PROXY_TABS, syncer::AUTOFILL, syncer::PREFERENCES,
syncer::READING_LIST,
};
} // namespace
SyncSetupService::SyncSetupService(syncer::SyncService* sync_service)
: sync_service_(sync_service) {
DCHECK(sync_service_);
}
SyncSetupService::~SyncSetupService() {}
// static
syncer::ModelType SyncSetupService::GetModelType(SyncableDatatype datatype) {
DCHECK(datatype < std::size(kDataTypes));
return kDataTypes[datatype];
}
syncer::ModelTypeSet SyncSetupService::GetPreferredDataTypes() const {
return sync_service_->GetPreferredDataTypes();
}
bool SyncSetupService::IsDataTypeActive(syncer::ModelType datatype) const {
return sync_service_->GetActiveDataTypes().Has(datatype);
}
bool SyncSetupService::IsDataTypePreferred(syncer::ModelType datatype) const {
return GetPreferredDataTypes().Has(datatype);
}
void SyncSetupService::SetDataTypeEnabled(syncer::ModelType datatype,
bool enabled) {
if (!sync_blocker_)
sync_blocker_ = sync_service_->GetSetupInProgressHandle();
syncer::ModelTypeSet model_types = GetPreferredDataTypes();
if (enabled)
model_types.Put(datatype);
else
model_types.Remove(datatype);
syncer::SyncUserSettings* user_settings = sync_service_->GetUserSettings();
// TODO(crbug.com/950874): support syncer::UserSelectableType in ios code,
// get rid of this workaround and consider getting rid of SyncableDatatype.
syncer::UserSelectableTypeSet selected_types;
for (syncer::UserSelectableType type :
user_settings->GetRegisteredSelectableTypes()) {
if (model_types.Has(syncer::UserSelectableTypeToCanonicalModelType(type))) {
selected_types.Put(type);
}
}
user_settings->SetSelectedTypes(IsSyncingAllDataTypes(), selected_types);
}
bool SyncSetupService::UserActionIsRequiredToHaveTabSyncWork() {
if (!CanSyncFeatureStart() || !IsDataTypePreferred(syncer::PROXY_TABS)) {
return true;
}
switch (this->GetSyncServiceState()) {
// No error.
case SyncSetupService::kNoSyncServiceError:
// These errors are transient and don't mean that sync is off.
case SyncSetupService::kSyncServiceCouldNotConnect:
case SyncSetupService::kSyncServiceServiceUnavailable:
case SyncSetupService::kSyncServiceTrustedVaultRecoverabilityDegraded:
return false;
// These errors effectively amount to disabled sync and require a signin.
case SyncSetupService::kSyncServiceSignInNeedsUpdate:
case SyncSetupService::kSyncServiceNeedsPassphrase:
case SyncSetupService::kSyncServiceUnrecoverableError:
return true;
case SyncSetupService::kSyncServiceNeedsTrustedVaultKey:
return IsEncryptEverythingEnabled();
}
NOTREACHED() << "Unknown sync service state.";
return true;
}
bool SyncSetupService::IsSyncingAllDataTypes() const {
return sync_service_->GetUserSettings()->IsSyncEverythingEnabled();
}
void SyncSetupService::SetSyncingAllDataTypes(bool sync_all) {
if (!sync_blocker_)
sync_blocker_ = sync_service_->GetSetupInProgressHandle();
sync_service_->GetUserSettings()->SetSelectedTypes(
sync_all, sync_service_->GetUserSettings()->GetSelectedTypes());
}
bool SyncSetupService::IsSyncRequested() const {
return sync_service_->GetUserSettings()->IsSyncRequested();
}
bool SyncSetupService::CanSyncFeatureStart() const {
return sync_service_->CanSyncFeatureStart();
}
void SyncSetupService::SetSyncEnabled(bool sync_enabled) {
if (!sync_blocker_)
sync_blocker_ = sync_service_->GetSetupInProgressHandle();
if (!sync_enabled) {
UMA_HISTOGRAM_ENUMERATION("Sync.StopSource", syncer::CHROME_SYNC_SETTINGS,
syncer::STOP_SOURCE_LIMIT);
}
sync_service_->GetUserSettings()->SetSyncRequested(sync_enabled);
if (sync_enabled && GetPreferredDataTypes().Empty())
SetSyncingAllDataTypes(true);
}
SyncSetupService::SyncServiceState SyncSetupService::GetSyncServiceState() {
switch (sync_service_->GetAuthError().state()) {
case GoogleServiceAuthError::REQUEST_CANCELED:
return kSyncServiceCouldNotConnect;
// TODO(crbug.com/1194007): This will support the SyncDisabled policy that
// can force the Sync service to become unavailable.
// Based on GetSyncStatusLabelsForAuthError, SERVICE_UNAVAILABLE
// corresponds to sync having been disabled for the user's domain.
case GoogleServiceAuthError::SERVICE_UNAVAILABLE:
return kSyncServiceServiceUnavailable;
case GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS:
return kSyncServiceSignInNeedsUpdate;
// The following errors are not shown to the user.
case GoogleServiceAuthError::NONE:
// Connection failed is not shown to the user, as this will happen if the
// service retuned a 500 error. A more detail error can always be checked
// on chrome://sync-internals.
case GoogleServiceAuthError::CONNECTION_FAILED:
case GoogleServiceAuthError::USER_NOT_SIGNED_UP:
case GoogleServiceAuthError::UNEXPECTED_SERVICE_RESPONSE:
break;
// The following errors are unexpected on iOS.
case GoogleServiceAuthError::SERVICE_ERROR:
case GoogleServiceAuthError::SCOPE_LIMITED_UNRECOVERABLE_ERROR:
// Conventional value for counting the states, never used.
case GoogleServiceAuthError::NUM_STATES:
NOTREACHED() << "Unexpected Auth error ("
<< sync_service_->GetAuthError().state()
<< "): " << sync_service_->GetAuthError().error_message();
break;
}
if (sync_service_->HasUnrecoverableError())
return kSyncServiceUnrecoverableError;
if (sync_service_->GetUserSettings()
->IsPassphraseRequiredForPreferredDataTypes()) {
return kSyncServiceNeedsPassphrase;
}
if (sync_service_->GetUserSettings()
->IsTrustedVaultKeyRequiredForPreferredDataTypes()) {
return kSyncServiceNeedsTrustedVaultKey;
}
if (sync_service_->GetUserSettings()
->IsTrustedVaultRecoverabilityDegraded()) {
return kSyncServiceTrustedVaultRecoverabilityDegraded;
}
return kNoSyncServiceError;
}
bool SyncSetupService::IsEncryptEverythingEnabled() const {
return sync_service_->GetUserSettings()->IsEncryptEverythingEnabled();
}
bool SyncSetupService::HasFinishedInitialSetup() {
// Sync initial setup is considered to finished iff:
// 1. User is signed in with sync enabled and the sync setup was completed.
// OR
// 2. User is not signed in or has disabled sync.
// Note that if the user visits the Advanced Settings during the opt-in flow,
// the Sync consent is not granted yet. In this case, IsSyncRequested() is
// set to true, indicating that the sync was requested but the initial setup
// has not been finished yet.
return !IsSyncRequested() ||
sync_service_->GetUserSettings()->IsFirstSetupComplete();
}
void SyncSetupService::PrepareForFirstSyncSetup() {
// |PrepareForFirstSyncSetup| should always be called while the user is signed
// out. At that time, sync setup is not completed.
DCHECK(!sync_service_->GetUserSettings()->IsFirstSetupComplete());
if (!sync_blocker_)
sync_blocker_ = sync_service_->GetSetupInProgressHandle();
}
void SyncSetupService::SetFirstSetupComplete(
syncer::SyncFirstSetupCompleteSource source) {
DCHECK(sync_blocker_);
// Turn on the sync setup completed flag only if the user did not turn sync
// off.
if (sync_service_->CanSyncFeatureStart()) {
sync_service_->GetUserSettings()->SetFirstSetupComplete(source);
}
}
bool SyncSetupService::IsFirstSetupComplete() const {
return sync_service_->GetUserSettings()->IsFirstSetupComplete();
}
void SyncSetupService::CommitSyncChanges() {
sync_blocker_.reset();
}
bool SyncSetupService::HasUncommittedChanges() {
return sync_service_->IsSetupInProgress();
}