blob: 07fa84910a2dfacaf1695b56d480bfcf61463ffd [file] [log] [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/privacy/privacy_metrics_service.h"
#include "base/metrics/histogram_functions.h"
#include "chrome/browser/prefetch/prefetch_prefs.h"
#include "chrome/common/pref_names.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/prefs/pref_service.h"
#include "components/spellcheck/browser/pref_names.h"
#include "components/sync/service/sync_service.h"
PrivacyMetricsService::PrivacyMetricsService(
PrefService* pref_service,
HostContentSettingsMap* host_content_settings_map,
syncer::SyncService* sync_service,
signin::IdentityManager* identity_manager)
: pref_service_(pref_service),
host_content_settings_map_(host_content_settings_map),
sync_service_(sync_service),
identity_manager_(identity_manager) {
DCHECK(pref_service_);
DCHECK(host_content_settings_map_);
RecordStartupMetrics();
// Avoid registering any observers if COE is disabled. Events which change
// COE state will still be caught on shutdown.
if (!IsClearOnExitEnabled())
return;
// Observe the identity manager regardless of sync state to catch changes to
// sync level consent (e.g. a user enabling sync).
if (identity_manager_)
identity_manager_observer_.Observe(identity_manager_.get());
// Avoid observing the sync service if sync-the-feature is disabled, events
// this service is interested in will still be caught by the identity manager
// observer. This service is not interested in sync-the-transport, as for the
// user visible sync-paused state to occur, sync-the-feature must be enabled.
if (!sync_service_ || !sync_service_->IsSyncFeatureEnabled()) {
RecordClearOnExitSyncEvent(ClearOnExitSyncEvent::kStartupSyncDisabled);
return;
}
sync_service_observer_.Observe(sync_service_.get());
// While this service is brought up with the profile, and thus practically
// should capture all sync state changes as the sync service starts up,
// firing the observer here ensures that no state changes are missed.
OnStateChanged(sync_service_);
}
PrivacyMetricsService::~PrivacyMetricsService() = default;
void PrivacyMetricsService::Shutdown() {
UnregisterObservers();
if (!sync_service_ || !IsClearOnExitEnabled()) {
return;
}
if (sync_service_->IsSyncFeatureActive()) {
ClearOnExitSyncEvent event;
if (sync_started_paused_ && primary_account_consent_changed_) {
event =
ClearOnExitSyncEvent::kShutdownSyncActiveStartedPausedConsentChange;
} else if (sync_started_paused_ && !primary_account_consent_changed_) {
event =
ClearOnExitSyncEvent::kShutdownSyncActiveStartedPausedNoConsentChange;
} else if (!sync_started_paused_ && primary_account_consent_changed_) {
event =
ClearOnExitSyncEvent::kShutdownSyncActiveStartedActiveConsentChange;
} else {
DCHECK(!sync_started_paused_ && !primary_account_consent_changed_);
event =
ClearOnExitSyncEvent::kShutdownSyncActiveStartedActiveNoConsentChange;
}
RecordClearOnExitSyncEvent(event);
} else if (sync_service_->GetTransportState() ==
syncer::SyncService::TransportState::PAUSED) {
RecordClearOnExitSyncEvent(ClearOnExitSyncEvent::kShutdownSyncPaused);
}
}
void PrivacyMetricsService::OnStateChanged(syncer::SyncService* sync) {
// This function will be called in response to all sync service state changes
// until sync reaches the active state and the observer is unregistered below.
// This may be soon after startup, or delayed if sync first enters the sync
// paused state (or experiences another uninteresting sync issue). This
// includes the initial state sync is in when this service is created, which
// as this service is created along with the profile is early enough that the
// user cannot have rectified a sync paused state.
if (sync->GetTransportState() ==
syncer::SyncService::TransportState::PAUSED &&
IsClearOnExitEnabled() && !sync_started_paused_) {
// Sync has started up directly into a paused state, very likely because of
// the users COE setting. Sync has not previously reached the active state,
// because when it does this observer is unregistered.
RecordClearOnExitSyncEvent(ClearOnExitSyncEvent::kStartupSyncPaused);
// Continue to observe the sync service, as if the user rectifies the
// sync paused error by signing directly back in, the account change
// observer wont trigger, but sync will eventually reach an active state.
sync_started_paused_ = true;
} else if (sync->GetTransportState() ==
syncer::SyncService::TransportState::ACTIVE) {
// Getting into the sync active state by changing primary account is handled
// by the identity manager observer.
if (!primary_account_consent_changed_ && IsClearOnExitEnabled()) {
if (sync_started_paused_) {
RecordClearOnExitSyncEvent(
ClearOnExitSyncEvent::kReloginToPausedAccount);
} else {
// User managed to avoid the sync paused state while having CoE enabled.
RecordClearOnExitSyncEvent(ClearOnExitSyncEvent::kStartupSyncActive);
}
}
// No further sync or identity updates are interesting. The final state of
// sync and COE will however be checked on Shutdown().
UnregisterObservers();
}
}
void PrivacyMetricsService::OnPrimaryAccountChanged(
const signin::PrimaryAccountChangeEvent& event_details) {
auto previous_consent_level = event_details.GetPreviousState().consent_level;
auto current_consent_level = event_details.GetCurrentState().consent_level;
if (current_consent_level != previous_consent_level) {
primary_account_consent_changed_ = true;
if (current_consent_level == signin::ConsentLevel::kSignin &&
IsClearOnExitEnabled() && sync_started_paused_) {
DCHECK(previous_consent_level == signin::ConsentLevel::kSync);
// The only way for a user to downgrade the consent level in the UI is
// by logging out of the account.
RecordClearOnExitSyncEvent(ClearOnExitSyncEvent::kLogoutOfPausedAccount);
identity_manager_observer_.Reset();
}
}
}
void PrivacyMetricsService::RecordStartupMetrics() {
base::UmaHistogramBoolean(
"Privacy.DoNotTrackSetting2",
pref_service_->GetBoolean(prefs::kEnableDoNotTrack));
base::UmaHistogramEnumeration("Settings.PreloadStatus.OnStartup3",
prefetch::GetPreloadPagesState(*pref_service_));
base::UmaHistogramBoolean(
"Settings.AutocompleteSearches.OnStartup2",
pref_service_->GetBoolean(::prefs::kSearchSuggestEnabled));
base::UmaHistogramBoolean(
"Settings.AdvancedSpellcheck.OnStartup2",
pref_service_->GetBoolean(
::spellcheck::prefs::kSpellCheckUseSpellingService));
}
void PrivacyMetricsService::UnregisterObservers() {
sync_service_observer_.Reset();
identity_manager_observer_.Reset();
}
bool PrivacyMetricsService::IsClearOnExitEnabled() {
return host_content_settings_map_->GetDefaultContentSetting(
ContentSettingsType::COOKIES,
/*provider_id=*/nullptr) ==
ContentSetting::CONTENT_SETTING_SESSION_ONLY;
}
void PrivacyMetricsService::RecordClearOnExitSyncEvent(
ClearOnExitSyncEvent event) const {
base::UmaHistogramEnumeration("Privacy.ClearOnExitSyncEvent", event);
}