blob: fb364a8532b3884562de278aace3d132a2165b36 [file] [log] [blame]
// 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/driver/sync_token_status.h"
#include "components/sync/engine/connection_status.h"
#include "components/unified_consent/feature.h"
#include "components/unified_consent/url_keyed_data_collection_consent_helper.h"
using unified_consent::UrlKeyedDataCollectionConsentHelper;
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,
DISABLED_BY_ANONYMIZED_DATA_COLLECTION,
MAX_DISABLE_INFO
};
void RecordDisableInfo(DisableInfo info) {
UMA_HISTOGRAM_ENUMERATION("UKM.SyncDisable.Info", info, MAX_DISABLE_INFO);
}
} // namespace
SyncDisableObserver::SyncDisableObserver() : sync_observer_(this) {}
SyncDisableObserver::~SyncDisableObserver() {
for (const auto& entry : consent_helpers_) {
entry.second->RemoveObserver(this);
}
}
bool SyncDisableObserver::SyncState::AllowsUkm() const {
if (anonymized_data_collection_state == DataCollectionState::kIgnored)
return history_enabled && initialized && connected && !passphrase_protected;
else
return anonymized_data_collection_state == DataCollectionState::kEnabled;
}
bool SyncDisableObserver::SyncState::AllowsUkmWithExtension() const {
return AllowsUkm() && extensions_enabled && initialized && connected &&
!passphrase_protected;
}
// static
SyncDisableObserver::SyncState SyncDisableObserver::GetSyncState(
syncer::SyncService* sync_service,
UrlKeyedDataCollectionConsentHelper* consent_helper) {
syncer::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();
if (consent_helper) {
state.anonymized_data_collection_state =
consent_helper->IsEnabled() ? DataCollectionState::kEnabled
: DataCollectionState::kDisabled;
}
return state;
}
void SyncDisableObserver::ObserveServiceForSyncDisables(
syncer::SyncService* sync_service,
PrefService* prefs) {
std::unique_ptr<UrlKeyedDataCollectionConsentHelper> consent_helper;
if (unified_consent::IsUnifiedConsentFeatureEnabled()) {
consent_helper = UrlKeyedDataCollectionConsentHelper::
NewAnonymizedDataCollectionConsentHelper(prefs, sync_service);
}
SyncState state = GetSyncState(sync_service, consent_helper.get());
previous_states_[sync_service] = state;
if (consent_helper) {
consent_helper->AddObserver(this);
consent_helpers_[sync_service] = std::move(consent_helper);
}
sync_observer_.Add(sync_service);
UpdateAllProfileEnabled(false);
}
void SyncDisableObserver::UpdateAllProfileEnabled(bool must_purge) {
bool all_sync_states_allow_ukm = CheckSyncStateOnAllProfiles();
bool all_sync_states_allow_extension_ukm =
all_sync_states_allow_ukm && CheckSyncStateForExtensionsOnAllProfiles();
// Any change in sync settings needs to call OnSyncPrefsChanged so that the
// new settings take effect.
if (must_purge || (all_sync_states_allow_ukm != all_sync_states_allow_ukm_) ||
(all_sync_states_allow_extension_ukm !=
all_sync_states_allow_extension_ukm_)) {
all_sync_states_allow_ukm_ = all_sync_states_allow_ukm;
all_sync_states_allow_extension_ukm_ = all_sync_states_allow_extension_ukm;
OnSyncPrefsChanged(must_purge);
}
}
bool SyncDisableObserver::CheckSyncStateOnAllProfiles() {
if (previous_states_.empty())
return false;
for (const auto& kv : previous_states_) {
const SyncDisableObserver::SyncState& state = kv.second;
if (!state.AllowsUkm()) {
int disabled_by = 0;
if (state.anonymized_data_collection_state ==
DataCollectionState::kIgnored) {
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;
} else {
DCHECK_EQ(DataCollectionState::kDisabled,
state.anonymized_data_collection_state);
disabled_by |= 1 << 4;
}
RecordDisableInfo(DisableInfo(disabled_by));
return false;
}
}
RecordDisableInfo(DISABLED_BY_NONE);
return true;
}
bool SyncDisableObserver::CheckSyncStateForExtensionsOnAllProfiles() {
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) {
UrlKeyedDataCollectionConsentHelper* consent_helper = nullptr;
auto found = consent_helpers_.find(sync);
if (found != consent_helpers_.end())
consent_helper = found->second.get();
UpdateSyncState(sync, consent_helper);
}
void SyncDisableObserver::OnUrlKeyedDataCollectionConsentStateChanged(
unified_consent::UrlKeyedDataCollectionConsentHelper* consent_helper) {
DCHECK(consent_helper);
syncer::SyncService* sync_service = nullptr;
for (const auto& entry : consent_helpers_) {
if (consent_helper == entry.second.get()) {
sync_service = entry.first;
break;
}
}
DCHECK(sync_service);
UpdateSyncState(sync_service, consent_helper);
}
void SyncDisableObserver::UpdateSyncState(
syncer::SyncService* sync,
UrlKeyedDataCollectionConsentHelper* consent_helper) {
DCHECK(base::ContainsKey(previous_states_, sync));
const SyncDisableObserver::SyncState& previous_state = previous_states_[sync];
DCHECK(previous_state.anonymized_data_collection_state ==
DataCollectionState::kIgnored ||
consent_helper);
SyncDisableObserver::SyncState state = GetSyncState(sync, consent_helper);
// Trigger a purge if sync state no longer allows UKM.
bool must_purge = previous_state.AllowsUkm() && !state.AllowsUkm();
previous_states_[sync] = state;
UpdateAllProfileEnabled(must_purge);
}
void SyncDisableObserver::OnSyncShutdown(syncer::SyncService* sync) {
DCHECK(base::ContainsKey(previous_states_, sync));
auto found = consent_helpers_.find(sync);
if (found != consent_helpers_.end()) {
found->second->RemoveObserver(this);
consent_helpers_.erase(found);
}
sync_observer_.Remove(sync);
previous_states_.erase(sync);
UpdateAllProfileEnabled(false);
}
bool SyncDisableObserver::SyncStateAllowsUkm() {
return all_sync_states_allow_ukm_;
}
bool SyncDisableObserver::SyncStateAllowsExtensionUkm() {
return all_sync_states_allow_extension_ukm_;
}
} // namespace ukm