blob: 463ed06c35a5a7c3ebd322ed1bcb765b1cdf87dd [file] [log] [blame]
// Copyright 2012 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/metrics/metrics_reporting_state.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/metrics/histogram_macros.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
#include "chrome/common/pref_names.h"
#include "chrome/installer/util/google_update_settings.h"
#include "components/crash/core/common/crash_keys.h"
#include "components/metrics/cloned_install_detector.h"
#include "components/metrics/entropy_state.h"
#include "components/metrics/metrics_pref_names.h"
#include "components/metrics/metrics_service.h"
#include "components/metrics_services_manager/metrics_services_manager.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/browser_thread.h"
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h"
#include "chrome/browser/browser_process_platform_part.h"
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
#if BUILDFLAG(IS_ANDROID)
#include "components/policy/core/common/features.h"
#endif // BUILDFLAG(IS_ANDROID)
namespace {
enum MetricsReportingChangeHistogramValue {
METRICS_REPORTING_ERROR,
METRICS_REPORTING_DISABLED,
METRICS_REPORTING_ENABLED,
METRICS_REPORTING_MAX
};
void RecordMetricsReportingHistogramValue(
MetricsReportingChangeHistogramValue value) {
UMA_HISTOGRAM_ENUMERATION(
"UMA.MetricsReporting.Toggle", value, METRICS_REPORTING_MAX);
}
// Tries to set metrics reporting status to |enabled| and returns whatever is
// the result of the update.
bool SetGoogleUpdateSettings(bool enabled) {
GoogleUpdateSettings::SetCollectStatsConsent(enabled);
bool updated_pref = GoogleUpdateSettings::GetCollectStatsConsent();
if (enabled != updated_pref)
DVLOG(1) << "Unable to set metrics reporting status to " << enabled;
return updated_pref;
}
// Does the necessary changes for MetricsReportingEnabled changes which needs
// to be done in the main thread.
// As arguments this function gets:
// |to_update_pref| which indicates what the desired update should be,
// |callback_fn| is the callback function to be called in the end,
// |called_from| is from where the call was made,
// |updated_pref| is the result of attempted update.
// Update considers to be successful if |to_update_pref| and |updated_pref| are
// the same.
void SetMetricsReporting(bool to_update_pref,
OnMetricsReportingCallbackType callback_fn,
ChangeMetricsReportingStateCalledFrom called_from,
bool updated_pref) {
g_browser_process->local_state()->SetBoolean(
metrics::prefs::kMetricsReportingEnabled, updated_pref);
UpdateMetricsPrefsOnPermissionChange(updated_pref, called_from);
// Uses the current state of whether reporting is enabled to enable services.
g_browser_process->GetMetricsServicesManager()->UpdateUploadPermissions(true);
if (to_update_pref == updated_pref) {
RecordMetricsReportingHistogramValue(updated_pref ?
METRICS_REPORTING_ENABLED : METRICS_REPORTING_DISABLED);
} else {
RecordMetricsReportingHistogramValue(METRICS_REPORTING_ERROR);
}
if (!callback_fn.is_null())
std::move(callback_fn).Run(updated_pref);
}
} // namespace
void ChangeMetricsReportingState(
bool enabled,
ChangeMetricsReportingStateCalledFrom called_from) {
ChangeMetricsReportingStateWithReply(
enabled, OnMetricsReportingCallbackType(), called_from);
}
// TODO(gayane): Instead of checking policy before setting the metrics pref set
// the pref and register for notifications for the rest of the changes.
void ChangeMetricsReportingStateWithReply(
bool enabled,
OnMetricsReportingCallbackType callback_fn,
ChangeMetricsReportingStateCalledFrom called_from) {
#if !BUILDFLAG(IS_ANDROID)
// Chrome OS manages metrics settings externally and changes to reporting
// should be propagated to metrics service regardless if the policy is managed
// or not.
// TODO(crbug.com/40232452): Possibly change |is_chrome_os| to use
// BUILDFLAG(IS_CHROMEOS_ASH).
bool is_chrome_os =
(called_from ==
ChangeMetricsReportingStateCalledFrom::kCrosMetricsSettingsChange) ||
(called_from ==
ChangeMetricsReportingStateCalledFrom::kCrosMetricsSettingsCreated);
if (IsMetricsReportingPolicyManaged() && !is_chrome_os) {
if (!callback_fn.is_null()) {
const bool metrics_enabled =
ChromeMetricsServiceAccessor::IsMetricsAndCrashReportingEnabled();
std::move(callback_fn).Run(metrics_enabled);
}
return;
}
#endif
GoogleUpdateSettings::CollectStatsConsentTaskRunner()
->PostTaskAndReplyWithResult(
FROM_HERE, base::BindOnce(&SetGoogleUpdateSettings, enabled),
base::BindOnce(&SetMetricsReporting, enabled, std::move(callback_fn),
called_from));
}
void UpdateMetricsPrefsOnPermissionChange(
bool metrics_enabled,
ChangeMetricsReportingStateCalledFrom called_from) {
// On Chrome OS settings creation, nothing should be performed (the metrics
// service is simply being initialized). Otherwise, for users who have
// metrics reporting disabled, their client ID and low entropy sources would
// be cleared on each log in. For users who have metrics reporting enabled,
// their stability metrics and histogram data would be cleared.
if (called_from ==
ChangeMetricsReportingStateCalledFrom::kCrosMetricsSettingsCreated) {
return;
}
if (metrics_enabled) {
// When a user opts in to the metrics reporting service, the previously
// collected data should be cleared to ensure that nothing is reported
// before a user opts in and all reported data is accurate.
g_browser_process->metrics_service()->ClearSavedStabilityMetrics();
if (called_from == ChangeMetricsReportingStateCalledFrom::kUiSettings) {
ClearPreviouslyCollectedMetricsData();
}
return;
}
#if BUILDFLAG(IS_ANDROID)
// When a user disables metrics reporting on Android Chrome, the new
// sampling trial should be used to determine whether the client is sampled
// in or out (if the user ever re-enables metrics reporting).
//
// Existing metrics-reporting-enabled clients (i.e. the users without this
// pref set) do not use the new sampling trial; they continue to use
// MetricsAndCrashSampling. However, if such a user disables metrics
// reporting and later re-enables it, they will start using the new trial.
//
// See crbug/1306481 and the comment above |kUsePostFREFixSamplingTrial| in
// components/metrics/metrics_pref_names.cc for more details.
g_browser_process->local_state()->SetBoolean(
metrics::prefs::kUsePostFREFixSamplingTrial, true);
#endif // BUILDFLAG(IS_ANDROID)
// Clear the client id and low entropy sources pref when the user opts out
// from a non-FRE source. In the FRE flow the entropy source is not cleared
// since no data has been uploaded yet.
// Note: This will not affect the running state (e.g. field trial
// randomization), as the pref is only read on startup.
UMA_HISTOGRAM_BOOLEAN("UMA.ClientIdCleared", true);
PrefService* local_state = g_browser_process->local_state();
local_state->ClearPref(metrics::prefs::kMetricsClientID);
local_state->ClearPref(metrics::prefs::kMetricsProvisionalClientID);
local_state->ClearPref(metrics::prefs::kMetricsLogRecordId);
// Don't clear the entropy state if the user opted out in the FRE. This is to
// prevent experiments that have been randomized based on the low-entropy
// source from having their state re-rolled on a subsequent session.
if (called_from != ChangeMetricsReportingStateCalledFrom::kUiFirstRun) {
metrics::EntropyState::ClearPrefs(local_state);
}
metrics::ClonedInstallDetector::ClearClonedInstallInfo(local_state);
local_state->ClearPref(metrics::prefs::kMetricsReportingEnabledTimestamp);
crash_keys::ClearMetricsClientId();
}
void ApplyMetricsReportingPolicy() {
GoogleUpdateSettings::CollectStatsConsentTaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
base::IgnoreResult(&GoogleUpdateSettings::SetCollectStatsConsent),
ChromeMetricsServiceAccessor::IsMetricsAndCrashReportingEnabled()));
}
bool IsMetricsReportingPolicyManaged() {
#if BUILDFLAG(IS_CHROMEOS_ASH)
policy::BrowserPolicyConnectorAsh* policy_connector =
g_browser_process->platform_part()->browser_policy_connector_ash();
return policy_connector->IsDeviceEnterpriseManaged();
#else
const PrefService* pref_service = g_browser_process->local_state();
const PrefService::Preference* pref =
pref_service->FindPreference(metrics::prefs::kMetricsReportingEnabled);
return pref && pref->IsManaged();
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
}
void ClearPreviouslyCollectedMetricsData() {
// Mark histogram data that was collected during the current session up until
// now as reported so that they are not included in the next log.
g_browser_process->metrics_service()->MarkCurrentHistogramsAsReported();
// Note: There is no need to clear User Actions as they do not get recorded
// when metrics reporting is disabled.
}