// Copyright 2017 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 "components/ukm/observers/sync_disable_observer.h" #include "base/feature_list.h" #include "base/metrics/histogram_macros.h" #include "base/stl_util.h" #include "components/sync/engine/connection_status.h" namespace ukm { const base::Feature kUkmCheckAuthErrorFeature{"UkmCheckAuthError", base::FEATURE_ENABLED_BY_DEFAULT}; namespace { enum DisableInfo { DISABLED_BY_NONE, DISABLED_BY_HISTORY, DISABLED_BY_INITIALIZED, DISABLED_BY_HISTORY_INITIALIZED, DISABLED_BY_CONNECTED, DISABLED_BY_HISTORY_CONNECTED, DISABLED_BY_INITIALIZED_CONNECTED, DISABLED_BY_HISTORY_INITIALIZED_CONNECTED, DISABLED_BY_PASSPHRASE, DISABLED_BY_HISTORY_PASSPHRASE, DISABLED_BY_INITIALIZED_PASSPHRASE, DISABLED_BY_HISTORY_INITIALIZED_PASSPHRASE, DISABLED_BY_CONNECTED_PASSPHRASE, DISABLED_BY_HISTORY_CONNECTED_PASSPHRASE, DISABLED_BY_INITIALIZED_CONNECTED_PASSPHRASE, DISABLED_BY_HISTORY_INITIALIZED_CONNECTED_PASSPHRASE, MAX_DISABLE_INFO }; void RecordDisableInfo(DisableInfo info) { UMA_HISTOGRAM_ENUMERATION("UKM.SyncDisable.Info", info, MAX_DISABLE_INFO); } } // namespace SyncDisableObserver::SyncDisableObserver() : sync_observer_(this), all_histories_enabled_(false), all_extensions_enabled_(false) {} SyncDisableObserver::~SyncDisableObserver() {} // static SyncDisableObserver::SyncState SyncDisableObserver::GetSyncState( syncer::SyncService* sync_service) { syncer::SyncService::SyncTokenStatus status = sync_service->GetSyncTokenStatus(); SyncState state; state.history_enabled = sync_service->GetPreferredDataTypes().Has( syncer::HISTORY_DELETE_DIRECTIVES); state.extensions_enabled = sync_service->GetPreferredDataTypes().Has(syncer::EXTENSIONS); state.initialized = sync_service->IsEngineInitialized(); state.connected = !base::FeatureList::IsEnabled(kUkmCheckAuthErrorFeature) || status.connection_status == syncer::CONNECTION_OK; state.passphrase_protected = state.initialized && sync_service->IsUsingSecondaryPassphrase(); return state; } void SyncDisableObserver::ObserveServiceForSyncDisables( syncer::SyncService* sync_service) { previous_states_[sync_service] = GetSyncState(sync_service); sync_observer_.Add(sync_service); UpdateAllProfileEnabled(false); } void SyncDisableObserver::UpdateAllProfileEnabled(bool must_purge) { bool all_enabled = CheckHistorySyncOnAllProfiles(); bool all_extensions_enabled = all_enabled && CheckExtensionSyncOnAllProfiles(); // Any change in sync settings needs to call OnSyncPrefsChanged so that the // new settings take effect. if (must_purge || (all_enabled != all_histories_enabled_) || (all_extensions_enabled != all_extensions_enabled_)) { all_histories_enabled_ = all_enabled; all_extensions_enabled_ = all_extensions_enabled; OnSyncPrefsChanged(must_purge); } } bool SyncDisableObserver::CheckHistorySyncOnAllProfiles() { if (previous_states_.empty()) return false; for (const auto& kv : previous_states_) { const SyncDisableObserver::SyncState& state = kv.second; if (!state.history_enabled || !state.initialized || !state.connected || state.passphrase_protected) { int disabled_by = 0; if (!state.history_enabled) disabled_by |= 1 << 0; if (!state.initialized) disabled_by |= 1 << 1; if (!state.connected) disabled_by |= 1 << 2; if (state.passphrase_protected) disabled_by |= 1 << 3; RecordDisableInfo(DisableInfo(disabled_by)); return false; } } RecordDisableInfo(DISABLED_BY_NONE); return true; } bool SyncDisableObserver::CheckExtensionSyncOnAllProfiles() { if (previous_states_.empty()) return false; for (const auto& kv : previous_states_) { const SyncDisableObserver::SyncState& state = kv.second; if (!state.extensions_enabled) return false; } return true; } void SyncDisableObserver::OnStateChanged(syncer::SyncService* sync) { DCHECK(base::ContainsKey(previous_states_, sync)); SyncDisableObserver::SyncState state = GetSyncState(sync); const SyncDisableObserver::SyncState& previous_state = previous_states_[sync]; bool must_purge = // Trigger a purge if history sync was disabled. (previous_state.history_enabled && !state.history_enabled) || // Trigger a purge if engine has become disabled. (previous_state.initialized && !state.initialized) || // Trigger a purge if the user added a passphrase. Since we can't detect // the use of a passphrase while the engine is not initialized, we may // miss the transition if the user adds a passphrase in this state. (previous_state.initialized && state.initialized && !previous_state.passphrase_protected && state.passphrase_protected); previous_states_[sync] = state; UpdateAllProfileEnabled(must_purge); } void SyncDisableObserver::OnSyncShutdown(syncer::SyncService* sync) { DCHECK(base::ContainsKey(previous_states_, sync)); sync_observer_.Remove(sync); previous_states_.erase(sync); UpdateAllProfileEnabled(false); } bool SyncDisableObserver::IsHistorySyncEnabledOnAllProfiles() { return all_histories_enabled_; } bool SyncDisableObserver::IsExtensionSyncEnabledOnAllProfiles() { return all_extensions_enabled_; } } // namespace ukm