summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorasvitkine@chromium.org <asvitkine@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-05 18:44:58 +0000
committerasvitkine@chromium.org <asvitkine@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-05 18:44:58 +0000
commitff1b9b212027c268b911d38cf1771c15adfefa5d (patch)
treee3a946c515c31d960d25270640abf081a17b6a05
parent6e84e2c187647c30f3f4563aac671ba9416b4182 (diff)
downloadchromium_src-ff1b9b212027c268b911d38cf1771c15adfefa5d.zip
chromium_src-ff1b9b212027c268b911d38cf1771c15adfefa5d.tar.gz
chromium_src-ff1b9b212027c268b911d38cf1771c15adfefa5d.tar.bz2
Refactor MetricsStateManager class out of MetricsService.
The new class is responsible for managing various MetricsService state prefs, such as client id, low entropy source and the UMA opt-in state, as well as the cloned install detector. Also, moves IsMetricsReportingEnabled() from chrome_browser_main.cc to the new class as well as changing a couple MetricsService browser tests to instead be unit tests of the new class. BUG=368413 Review URL: https://codereview.chromium.org/256143006 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@268232 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/chrome_browser_main.cc51
-rw-r--r--chrome/browser/chrome_browser_main.h3
-rw-r--r--chrome/browser/metrics/cloned_install_detector_unittest.cc5
-rw-r--r--chrome/browser/metrics/metrics_service.cc206
-rw-r--r--chrome/browser/metrics/metrics_service.h92
-rw-r--r--chrome/browser/metrics/metrics_service_browsertest.cc28
-rw-r--r--chrome/browser/metrics/metrics_service_unittest.cc132
-rw-r--r--chrome/browser/metrics/metrics_state_manager.cc233
-rw-r--r--chrome/browser/metrics/metrics_state_manager.h124
-rw-r--r--chrome/browser/metrics/metrics_state_manager_unittest.cc160
-rw-r--r--chrome/browser/prefs/browser_prefs.cc4
-rw-r--r--chrome/chrome_browser.gypi2
-rw-r--r--chrome/chrome_tests_unit.gypi1
13 files changed, 596 insertions, 445 deletions
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index 19c4cad..d84585d 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -562,14 +562,8 @@ void ChromeBrowserMainParts::SetupMetricsAndFieldTrials() {
// Initialize FieldTrialList to support FieldTrials that use one-time
// randomization.
MetricsService* metrics = browser_process_->metrics_service();
- MetricsService::ReportingState reporting_state =
- IsMetricsReportingEnabled() ? MetricsService::REPORTING_ENABLED :
- MetricsService::REPORTING_DISABLED;
- if (reporting_state == MetricsService::REPORTING_ENABLED)
- metrics->ForceClientIdCreation(); // Needed below.
field_trial_list_.reset(
- new base::FieldTrialList(
- metrics->CreateEntropyProvider(reporting_state).release()));
+ new base::FieldTrialList(metrics->CreateEntropyProvider().release()));
const CommandLine* command_line = CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kEnableBenchmarking))
@@ -617,7 +611,7 @@ void ChromeBrowserMainParts::SetupMetricsAndFieldTrials() {
field_trial_synchronizer_ = new FieldTrialSynchronizer();
// Now that field trials have been created, initializes metrics recording.
- metrics->InitializeMetricsRecordingState(reporting_state);
+ metrics->InitializeMetricsRecordingState();
}
// ChromeBrowserMainParts: |SetupMetricsAndFieldTrials()| related --------------
@@ -637,33 +631,14 @@ void ChromeBrowserMainParts::StartMetricsRecording() {
}
metrics->CheckForClonedInstall();
-
- if (IsMetricsReportingEnabled())
- metrics->Start();
-}
-
-bool ChromeBrowserMainParts::IsMetricsReportingEnabled() {
- // If the user permits metrics reporting with the checkbox in the
- // prefs, we turn on recording. We disable metrics completely for
- // non-official builds. This can be forced with a flag.
- const CommandLine* command_line = CommandLine::ForCurrentProcess();
- if (command_line->HasSwitch(switches::kEnableMetricsReportingForTesting))
- return true;
-
- bool enabled = false;
- // The debug build doesn't send UMA logs when FieldTrials are forced.
- if (command_line->HasSwitch(switches::kForceFieldTrials))
- return false;
-
-#if defined(GOOGLE_CHROME_BUILD)
-#if defined(OS_CHROMEOS)
- chromeos::CrosSettings::Get()->GetBoolean(chromeos::kStatsReportingPref,
- &enabled);
-#else
- enabled = local_state_->GetBoolean(prefs::kMetricsReportingEnabled);
-#endif // #if defined(OS_CHROMEOS)
-#endif // defined(GOOGLE_CHROME_BUILD)
- return enabled;
+ const bool metrics_enabled = metrics->StartIfMetricsReportingEnabled();
+ if (metrics_enabled) {
+ // TODO(asvitkine): Since this function is not run on Android, RAPPOR is
+ // currently disabled there. http://crbug.com/370041
+ browser_process_->rappor_service()->Start(
+ browser_process_->local_state(),
+ browser_process_->system_request_context());
+ }
}
void ChromeBrowserMainParts::RecordBrowserStartupTime() {
@@ -1094,12 +1069,6 @@ int ChromeBrowserMainParts::PreMainMessageLoopRunImpl() {
StartMetricsRecording();
#endif
- if (IsMetricsReportingEnabled()) {
- browser_process_->rappor_service()->Start(
- browser_process_->local_state(),
- browser_process_->system_request_context());
- }
-
// Create watchdog thread after creating all other threads because it will
// watch the other threads and they must be running.
browser_process_->watchdog_thread();
diff --git a/chrome/browser/chrome_browser_main.h b/chrome/browser/chrome_browser_main.h
index 0d1b51c..a856a2b7 100644
--- a/chrome/browser/chrome_browser_main.h
+++ b/chrome/browser/chrome_browser_main.h
@@ -107,9 +107,6 @@ class ChromeBrowserMainParts : public content::BrowserMainParts {
// thread.
void StartMetricsRecording();
- // Returns true if the user opted in to sending metric reports.
- bool IsMetricsReportingEnabled();
-
// Record time from process startup to present time in an UMA histogram.
void RecordBrowserStartupTime();
diff --git a/chrome/browser/metrics/cloned_install_detector_unittest.cc b/chrome/browser/metrics/cloned_install_detector_unittest.cc
index dcc8bf6..6114632 100644
--- a/chrome/browser/metrics/cloned_install_detector_unittest.cc
+++ b/chrome/browser/metrics/cloned_install_detector_unittest.cc
@@ -6,7 +6,7 @@
#include "base/prefs/testing_pref_service.h"
#include "chrome/browser/metrics/machine_id_provider.h"
-#include "chrome/browser/metrics/metrics_service.h"
+#include "chrome/browser/metrics/metrics_state_manager.h"
#include "chrome/common/pref_names.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -37,8 +37,7 @@ TEST(ClonedInstallDetectorTest, SaveId) {
TEST(ClonedInstallDetectorTest, DetectClone) {
TestingPrefServiceSimple prefs;
- ClonedInstallDetector::RegisterPrefs(prefs.registry());
- MetricsService::RegisterPrefs(prefs.registry());
+ MetricsStateManager::RegisterPrefs(prefs.registry());
// Save a machine id that will cause a clone to be detected.
prefs.SetInteger(prefs::kMetricsMachineId, kTestHashedId + 1);
diff --git a/chrome/browser/metrics/metrics_service.cc b/chrome/browser/metrics/metrics_service.cc
index 55c1321..8f56fa1 100644
--- a/chrome/browser/metrics/metrics_service.cc
+++ b/chrome/browser/metrics/metrics_service.cc
@@ -168,14 +168,12 @@
#include "base/bind.h"
#include "base/callback.h"
#include "base/command_line.h"
-#include "base/guid.h"
#include "base/metrics/histogram.h"
#include "base/metrics/sparse_histogram.h"
#include "base/metrics/statistics_recorder.h"
#include "base/prefs/pref_registry_simple.h"
#include "base/prefs/pref_service.h"
#include "base/prefs/scoped_user_pref_update.h"
-#include "base/rand_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/platform_thread.h"
@@ -187,12 +185,11 @@
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/io_thread.h"
#include "chrome/browser/memory_details.h"
-#include "chrome/browser/metrics/cloned_install_detector.h"
#include "chrome/browser/metrics/compression_utils.h"
-#include "chrome/browser/metrics/machine_id_provider.h"
#include "chrome/browser/metrics/metrics_log.h"
#include "chrome/browser/metrics/metrics_log_serializer.h"
#include "chrome/browser/metrics/metrics_reporting_scheduler.h"
+#include "chrome/browser/metrics/metrics_state_manager.h"
#include "chrome/browser/metrics/time_ticks_experiment_win.h"
#include "chrome/browser/metrics/tracking_synchronizer.h"
#include "chrome/common/metrics/variations/variations_util.h"
@@ -206,7 +203,6 @@
#include "chrome/common/chrome_result_codes.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/crash_keys.h"
-#include "chrome/common/metrics/caching_permuted_entropy_provider.h"
#include "chrome/common/net/test_server_locations.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/render_messages.h"
@@ -313,21 +309,6 @@ ResponseStatus ResponseCodeToStatus(int response_code) {
}
}
-// The argument used to generate a non-identifying entropy source. We want no
-// more than 13 bits of entropy, so use this max to return a number in the range
-// [0, 7999] as the entropy source (12.97 bits of entropy).
-const int kMaxLowEntropySize = 8000;
-
-// Default prefs value for prefs::kMetricsLowEntropySource to indicate that the
-// value has not yet been set.
-const int kLowEntropySourceNotSet = -1;
-
-// Generates a new non-identifying entropy source used to seed persistent
-// activities.
-int GenerateLowEntropySource() {
- return base::RandInt(0, kMaxLowEntropySize - 1);
-}
-
// Converts an exit code into something that can be inserted into our
// histograms (which expect non-negative numbers less than MAX_INT).
int MapCrashExitCodeForHistogram(int exit_code) {
@@ -430,11 +411,8 @@ class MetricsMemoryDetails : public MemoryDetails {
// static
void MetricsService::RegisterPrefs(PrefRegistrySimple* registry) {
DCHECK(IsSingleThreaded());
- registry->RegisterBooleanPref(prefs::kMetricsResetIds, false);
- registry->RegisterStringPref(prefs::kMetricsClientID, std::string());
- registry->RegisterInt64Pref(prefs::kMetricsReportingEnabledTimestamp, 0);
- registry->RegisterIntegerPref(prefs::kMetricsLowEntropySource,
- kLowEntropySourceNotSet);
+ metrics::MetricsStateManager::RegisterPrefs(registry);
+
registry->RegisterInt64Pref(prefs::kStabilityLaunchTimeSec, 0);
registry->RegisterInt64Pref(prefs::kStabilityLastTimestampSec, 0);
registry->RegisterStringPref(prefs::kStabilityStatsVersion, std::string());
@@ -479,33 +457,28 @@ void MetricsService::RegisterPrefs(PrefRegistrySimple* registry) {
registry->RegisterInt64Pref(prefs::kUninstallLastLaunchTimeSec, 0);
registry->RegisterInt64Pref(prefs::kUninstallLastObservedRunTimeSec, 0);
- // TODO(asvitkine): Remove these once a couple of releases have passed.
- // http://crbug.com/357704
- registry->RegisterStringPref(prefs::kMetricsOldClientID, std::string());
- registry->RegisterIntegerPref(prefs::kMetricsOldLowEntropySource, 0);
-
#if defined(OS_ANDROID)
RegisterPrefsAndroid(registry);
#endif // defined(OS_ANDROID)
}
MetricsService::MetricsService()
- : metrics_ids_reset_check_performed_(false),
+ : state_manager_(metrics::MetricsStateManager::Create(
+ g_browser_process->local_state())),
recording_active_(false),
reporting_active_(false),
test_mode_active_(false),
state_(INITIALIZED),
has_initial_stability_log_(false),
- low_entropy_source_(kLowEntropySourceNotSet),
idle_since_last_transmission_(false),
session_id_(-1),
next_window_id_(0),
self_ptr_factory_(this),
state_saver_factory_(this),
waiting_for_asynchronous_reporting_step_(false),
- num_async_histogram_fetches_in_progress_(0),
- entropy_source_returned_(LAST_ENTROPY_NONE) {
+ num_async_histogram_fetches_in_progress_(0) {
DCHECK(IsSingleThreaded());
+ DCHECK(state_manager_);
log_manager_.set_log_serializer(new MetricsLogSerializer);
log_manager_.set_max_ongoing_log_store_size(kUploadLogAvoidRetransmitSize);
@@ -519,9 +492,8 @@ MetricsService::~MetricsService() {
BrowserChildProcessObserver::Remove(this);
}
-void MetricsService::InitializeMetricsRecordingState(
- ReportingState reporting_state) {
- InitializeMetricsState(reporting_state);
+void MetricsService::InitializeMetricsRecordingState() {
+ InitializeMetricsState();
base::Closure callback = base::Bind(&MetricsService::StartScheduledUpload,
self_ptr_factory_.GetWeakPtr());
@@ -534,6 +506,13 @@ void MetricsService::Start() {
EnableReporting();
}
+bool MetricsService::StartIfMetricsReportingEnabled() {
+ const bool enabled = state_manager_->IsMetricsReportingEnabled();
+ if (enabled)
+ Start();
+ return enabled;
+}
+
void MetricsService::StartRecordingForTests() {
test_mode_active_ = true;
EnableRecording();
@@ -558,70 +537,14 @@ void MetricsService::DisableReporting() {
}
std::string MetricsService::GetClientId() {
- return client_id_;
+ return state_manager_->client_id();
}
scoped_ptr<const base::FieldTrial::EntropyProvider>
-MetricsService::CreateEntropyProvider(ReportingState reporting_state) {
- // For metrics reporting-enabled users, we combine the client ID and low
- // entropy source to get the final entropy source. Otherwise, only use the low
- // entropy source.
- // This has two useful properties:
- // 1) It makes the entropy source less identifiable for parties that do not
- // know the low entropy source.
- // 2) It makes the final entropy source resettable.
- const int low_entropy_source_value = GetLowEntropySource();
- UMA_HISTOGRAM_SPARSE_SLOWLY("UMA.LowEntropySourceValue",
- low_entropy_source_value);
- if (reporting_state == REPORTING_ENABLED) {
- if (entropy_source_returned_ == LAST_ENTROPY_NONE)
- entropy_source_returned_ = LAST_ENTROPY_HIGH;
- DCHECK_EQ(LAST_ENTROPY_HIGH, entropy_source_returned_);
- const std::string high_entropy_source =
- client_id_ + base::IntToString(low_entropy_source_value);
- return scoped_ptr<const base::FieldTrial::EntropyProvider>(
- new metrics::SHA1EntropyProvider(high_entropy_source));
- }
-
- if (entropy_source_returned_ == LAST_ENTROPY_NONE)
- entropy_source_returned_ = LAST_ENTROPY_LOW;
- DCHECK_EQ(LAST_ENTROPY_LOW, entropy_source_returned_);
-
-#if defined(OS_ANDROID) || defined(OS_IOS)
- return scoped_ptr<const base::FieldTrial::EntropyProvider>(
- new metrics::CachingPermutedEntropyProvider(
- g_browser_process->local_state(),
- low_entropy_source_value,
- kMaxLowEntropySize));
-#else
- return scoped_ptr<const base::FieldTrial::EntropyProvider>(
- new metrics::PermutedEntropyProvider(low_entropy_source_value,
- kMaxLowEntropySize));
-#endif
-}
-
-void MetricsService::ForceClientIdCreation() {
- if (!client_id_.empty())
- return;
-
- ResetMetricsIDsIfNecessary();
-
- PrefService* pref = g_browser_process->local_state();
- client_id_ = pref->GetString(prefs::kMetricsClientID);
- if (!client_id_.empty())
- return;
-
- client_id_ = GenerateClientID();
- pref->SetString(prefs::kMetricsClientID, client_id_);
-
- if (pref->GetString(prefs::kMetricsOldClientID).empty()) {
- // Record the timestamp of when the user opted in to UMA.
- pref->SetInt64(prefs::kMetricsReportingEnabledTimestamp,
- Time::Now().ToTimeT());
- } else {
- UMA_HISTOGRAM_BOOLEAN("UMA.ClientIdMigrated", true);
- }
- pref->ClearPref(prefs::kMetricsOldClientID);
+MetricsService::CreateEntropyProvider() {
+ // TODO(asvitkine): Refactor the code so that MetricsService does not expose
+ // this method.
+ return state_manager_->CreateEntropyProvider();
}
void MetricsService::EnableRecording() {
@@ -631,8 +554,8 @@ void MetricsService::EnableRecording() {
return;
recording_active_ = true;
- ForceClientIdCreation();
- crash_keys::SetClientID(client_id_);
+ state_manager_->ForceClientIdCreation();
+ crash_keys::SetClientID(state_manager_->client_id());
if (!log_manager_.current_log())
OpenNewLog();
@@ -918,7 +841,7 @@ void MetricsService::CountBrowserCrashDumpAttempts() {
//------------------------------------------------------------------------------
// Initialization methods
-void MetricsService::InitializeMetricsState(ReportingState reporting_state) {
+void MetricsService::InitializeMetricsState() {
#if defined(OS_POSIX)
network_stats_server_ = chrome_common_net::kEchoTestServerLocation;
http_pipelining_test_server_ = chrome_common_net::kPipelineTestServerBaseUrl;
@@ -955,7 +878,7 @@ void MetricsService::InitializeMetricsState(ReportingState reporting_state) {
// If the previous session didn't exit cleanly, then prepare an initial
// stability log if UMA is enabled.
- if (reporting_state == REPORTING_ENABLED)
+ if (state_manager_->IsMetricsReportingEnabled())
PrepareInitialStabilityLog();
}
@@ -1119,7 +1042,8 @@ void MetricsService::ReceivedProfilerData(
// save the profiler data.
if (!initial_metrics_log_.get()) {
initial_metrics_log_.reset(
- new MetricsLog(client_id_, session_id_, MetricsLog::ONGOING_LOG));
+ new MetricsLog(state_manager_->client_id(), session_id_,
+ MetricsLog::ONGOING_LOG));
}
initial_metrics_log_->RecordProfilerData(process_data, process_type);
@@ -1153,64 +1077,6 @@ void MetricsService::GetUptimes(PrefService* pref,
}
}
-void MetricsService::ResetMetricsIDsIfNecessary() {
- if (metrics_ids_reset_check_performed_)
- return;
-
- metrics_ids_reset_check_performed_ = true;
-
- PrefService* local_state = g_browser_process->local_state();
- if (!local_state->GetBoolean(prefs::kMetricsResetIds))
- return;
-
- UMA_HISTOGRAM_BOOLEAN("UMA.MetricsIDsReset", true);
-
- DCHECK(client_id_.empty());
- DCHECK_EQ(kLowEntropySourceNotSet, low_entropy_source_);
-
- local_state->ClearPref(prefs::kMetricsClientID);
- local_state->ClearPref(prefs::kMetricsLowEntropySource);
- local_state->ClearPref(prefs::kMetricsResetIds);
-}
-
-int MetricsService::GetLowEntropySource() {
- // Note that the default value for the low entropy source and the default pref
- // value are both kLowEntropySourceNotSet, which is used to identify if the
- // value has been set or not.
- if (low_entropy_source_ != kLowEntropySourceNotSet)
- return low_entropy_source_;
-
- ResetMetricsIDsIfNecessary();
-
- PrefService* local_state = g_browser_process->local_state();
- const CommandLine* command_line(CommandLine::ForCurrentProcess());
- // Only try to load the value from prefs if the user did not request a reset.
- // Otherwise, skip to generating a new value.
- if (!command_line->HasSwitch(switches::kResetVariationState)) {
- int value = local_state->GetInteger(prefs::kMetricsLowEntropySource);
- // If the value is outside the [0, kMaxLowEntropySize) range, re-generate
- // it below.
- if (value >= 0 && value < kMaxLowEntropySize) {
- low_entropy_source_ = value;
- UMA_HISTOGRAM_BOOLEAN("UMA.GeneratedLowEntropySource", false);
- return low_entropy_source_;
- }
- }
-
- UMA_HISTOGRAM_BOOLEAN("UMA.GeneratedLowEntropySource", true);
- low_entropy_source_ = GenerateLowEntropySource();
- local_state->SetInteger(prefs::kMetricsLowEntropySource, low_entropy_source_);
- local_state->ClearPref(prefs::kMetricsOldLowEntropySource);
- metrics::CachingPermutedEntropyProvider::ClearCache(local_state);
-
- return low_entropy_source_;
-}
-
-// static
-std::string MetricsService::GenerateClientID() {
- return base::GenerateGUID();
-}
-
//------------------------------------------------------------------------------
// State save methods
@@ -1244,7 +1110,8 @@ void MetricsService::OpenNewLog() {
DCHECK(!log_manager_.current_log());
log_manager_.BeginLoggingWithLog(
- new MetricsLog(client_id_, session_id_, MetricsLog::ONGOING_LOG));
+ new MetricsLog(state_manager_->client_id(), session_id_,
+ MetricsLog::ONGOING_LOG));
if (state_ == INITIALIZED) {
// We only need to schedule that run once.
state_ = INIT_TASK_SCHEDULED;
@@ -1540,7 +1407,7 @@ void MetricsService::PrepareInitialStabilityLog() {
DCHECK_NE(0, pref->GetInteger(prefs::kStabilityCrashCount));
scoped_ptr<MetricsLog> initial_stability_log(
- new MetricsLog(client_id_, session_id_,
+ new MetricsLog(state_manager_->client_id(), session_id_,
MetricsLog::INITIAL_STABILITY_LOG));
if (!initial_stability_log->LoadSavedEnvironmentFromPrefs())
return;
@@ -1839,18 +1706,7 @@ void MetricsService::RegisterSyntheticFieldTrial(
}
void MetricsService::CheckForClonedInstall() {
- DCHECK(!cloned_install_detector_);
-
- metrics::MachineIdProvider* provider =
- metrics::MachineIdProvider::CreateInstance();
- if (!provider)
- return;
-
- cloned_install_detector_.reset(
- new metrics::ClonedInstallDetector(provider));
-
- PrefService* local_state = g_browser_process->local_state();
- cloned_install_detector_->CheckForClonedInstall(local_state);
+ state_manager_->CheckForClonedInstall();
}
void MetricsService::GetCurrentSyntheticFieldTrials(
diff --git a/chrome/browser/metrics/metrics_service.h b/chrome/browser/metrics/metrics_service.h
index 82c3c30..1499ddf 100644
--- a/chrome/browser/metrics/metrics_service.h
+++ b/chrome/browser/metrics/metrics_service.h
@@ -69,7 +69,7 @@ class MetricsPrivateGetIsCrashReportingEnabledFunction;
}
namespace metrics {
-class ClonedInstallDetector;
+class MetricsStateManager;
}
namespace net {
@@ -127,26 +127,24 @@ class MetricsService
SHUTDOWN_COMPLETE = 700,
};
- enum ReportingState {
- REPORTING_ENABLED,
- REPORTING_DISABLED,
- };
-
MetricsService();
virtual ~MetricsService();
// Initializes metrics recording state. Updates various bookkeeping values in
// prefs and sets up the scheduler. This is a separate function rather than
// being done by the constructor so that field trials could be created before
- // this is run. Takes |reporting_state| parameter which specifies whether UMA
- // is enabled.
- void InitializeMetricsRecordingState(ReportingState reporting_state);
+ // this is run.
+ void InitializeMetricsRecordingState();
// Starts the metrics system, turning on recording and uploading of metrics.
// Should be called when starting up with metrics enabled, or when metrics
// are turned on.
void Start();
+ // If metrics reporting is enabled, starts the metrics service. Returns
+ // whether the metrics service was started.
+ bool StartIfMetricsReportingEnabled();
+
// Starts the metrics system in a special test-only mode. Metrics won't ever
// be uploaded or persisted in this mode, but metrics will be recorded in
// memory.
@@ -169,23 +167,12 @@ class MetricsService
// Returns the preferred entropy provider used to seed persistent activities
// based on whether or not metrics reporting will be permitted on this client.
- // The caller must determine if metrics reporting will be enabled for this
- // client and pass that state in as |reporting_will_be_enabled|.
- //
- // If |reporting_will_be_enabled| is true, this method returns an entropy
- // provider that has a high source of entropy, partially based on the client
- // ID. Otherwise, an entropy provider that is based on a low entropy source
- // is returned.
//
- // Note that this reporting state can not be checked by reporting_active()
- // because this method may need to be called before the MetricsService needs
- // to be started.
- scoped_ptr<const base::FieldTrial::EntropyProvider> CreateEntropyProvider(
- ReportingState reporting_state);
-
- // Force the client ID to be generated. This is useful in case it's needed
- // before recording.
- void ForceClientIdCreation();
+ // If metrics reporting is enabled, this method returns an entropy provider
+ // that has a high source of entropy, partially based on the client ID.
+ // Otherwise, it returns an entropy provider that is based on a low entropy
+ // source.
+ scoped_ptr<const base::FieldTrial::EntropyProvider> CreateEntropyProvider();
// At startup, prefs needs to be called with a list of all the pref names and
// types we'll be using.
@@ -312,15 +299,6 @@ class MetricsService
NEED_TO_SHUTDOWN = ~CLEANLY_SHUTDOWN
};
- // Designates which entropy source was returned from this MetricsService.
- // This is used for testing to validate that we return the correct source
- // depending on the state of the service.
- enum EntropySourceReturned {
- LAST_ENTROPY_NONE,
- LAST_ENTROPY_LOW,
- LAST_ENTROPY_HIGH,
- };
-
struct ChildProcessStats;
typedef std::vector<SyntheticTrialGroup> SyntheticTrialGroups;
@@ -368,22 +346,6 @@ class MetricsService
base::TimeDelta* incremental_uptime,
base::TimeDelta* uptime);
- // Reset the client id and low entropy source if the kMetricsResetMetricIDs
- // pref is true.
- void ResetMetricsIDsIfNecessary();
-
- // Returns the low entropy source for this client. This is a random value
- // that is non-identifying amongst browser clients. This method will
- // generate the entropy source value if it has not been called before.
- int GetLowEntropySource();
-
- // Returns the first entropy source that was returned by this service since
- // start up, or NONE if neither was returned yet. This is exposed for testing
- // only.
- EntropySourceReturned entropy_source_returned() const {
- return entropy_source_returned_;
- }
-
// Turns recording on or off.
// DisableRecording() also forces a persistent save of logging state (if
// anything has been recorded, or transmitted).
@@ -397,10 +359,7 @@ class MetricsService
void HandleIdleSinceLastTransmission(bool in_idle);
// Set up client ID, session ID, etc.
- void InitializeMetricsState(ReportingState reporting_state);
-
- // Generates a new client ID to use to identify self to metrics server.
- static std::string GenerateClientID();
+ void InitializeMetricsState();
// Schedule the next save of LocalState information. This is called
// automatically by the task that performs each save to schedule the next one.
@@ -512,6 +471,10 @@ class MetricsService
void GetCurrentSyntheticFieldTrials(
std::vector<chrome_variations::ActiveGroupId>* synthetic_trials);
+ // Used to manage various metrics reporting state prefs, such as client id,
+ // low entropy source and whether metrics reporting is enabled.
+ scoped_ptr<metrics::MetricsStateManager> state_manager_;
+
base::ActionCallback action_callback_;
content::NotificationRegistrar registrar_;
@@ -563,12 +526,6 @@ class MetricsService
// The HTTP pipelining test server.
std::string http_pipelining_test_server_;
- // The identifier that's sent to the server with the log reports.
- std::string client_id_;
-
- // The non-identifying low entropy source value.
- int low_entropy_source_;
-
// Whether the MetricsService object has received any notifications since
// the last time a transmission was sent.
bool idle_since_last_transmission_;
@@ -609,9 +566,6 @@ class MetricsService
scoped_refptr<chromeos::ExternalMetrics> external_metrics_;
#endif
- // The last entropy source returned by this service, used for testing.
- EntropySourceReturned entropy_source_returned_;
-
// Stores the time of the first call to |GetUptimes()|.
base::TimeTicks first_updated_time_;
@@ -628,19 +582,10 @@ class MetricsService
// Field trial groups that map to Chrome configuration states.
SyntheticTrialGroups synthetic_trial_groups_;
- scoped_ptr<metrics::ClonedInstallDetector> cloned_install_detector_;
-
- FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, ClientIdCorrectlyFormatted);
FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, IsPluginProcess);
- FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, LowEntropySource0NotReset);
FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest,
PermutedEntropyCacheClearedWhenLowEntropyReset);
FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, RegisterSyntheticTrial);
- FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, ResetMetricsIDs);
- FRIEND_TEST_ALL_PREFIXES(MetricsServiceBrowserTest,
- CheckLowEntropySourceUsed);
- FRIEND_TEST_ALL_PREFIXES(MetricsServiceReportingTest,
- CheckHighEntropySourceUsed);
DISALLOW_COPY_AND_ASSIGN(MetricsService);
};
@@ -662,6 +607,9 @@ class MetricsServiceHelper {
FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, CrashReportingEnabled);
// Returns true if prefs::kMetricsReportingEnabled is set.
+ // TODO(asvitkine): Consolidate the method in MetricsStateManager.
+ // TODO(asvitkine): This function does not report the correct value on
+ // Android and ChromeOS, see http://crbug.com/362192.
static bool IsMetricsReportingEnabled();
// Returns true if crash reporting is enabled. This is set at the platform
diff --git a/chrome/browser/metrics/metrics_service_browsertest.cc b/chrome/browser/metrics/metrics_service_browsertest.cc
index 666afae..d87f5b7 100644
--- a/chrome/browser/metrics/metrics_service_browsertest.cc
+++ b/chrome/browser/metrics/metrics_service_browsertest.cc
@@ -5,6 +5,8 @@
// Tests the MetricsService stat recording to make sure that the numbers are
// what we expect.
+#include "chrome/browser/metrics/metrics_service.h"
+
#include <string>
#include "base/command_line.h"
@@ -12,7 +14,6 @@
#include "base/path_service.h"
#include "base/prefs/pref_service.h"
#include "chrome/browser/browser_process.h"
-#include "chrome/browser/metrics/metrics_service.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/chrome_paths.h"
@@ -58,14 +59,6 @@ class MetricsServiceBrowserTest : public InProcessBrowserTest {
}
};
-class MetricsServiceReportingTest : public InProcessBrowserTest {
- public:
- virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
- // Enable the metrics service for testing (in the full mode).
- command_line->AppendSwitch(switches::kEnableMetricsReportingForTesting);
- }
-};
-
IN_PROC_BROWSER_TEST_F(MetricsServiceBrowserTest, CloseRenderersNormally) {
OpenTabs();
@@ -115,20 +108,3 @@ IN_PROC_BROWSER_TEST_F(MetricsServiceBrowserTest, MAYBE_CrashRenderers) {
// exits... it's not clear to me how to test that.
}
-IN_PROC_BROWSER_TEST_F(MetricsServiceBrowserTest, CheckLowEntropySourceUsed) {
- // Since MetricsService is only in recording mode, and is not reporting,
- // check that the low entropy source is returned at some point.
- ASSERT_TRUE(g_browser_process->metrics_service());
- EXPECT_EQ(MetricsService::LAST_ENTROPY_LOW,
- g_browser_process->metrics_service()->entropy_source_returned());
-}
-
-IN_PROC_BROWSER_TEST_F(MetricsServiceReportingTest,
- CheckHighEntropySourceUsed) {
- // Since the full metrics service runs in this test, we expect that
- // MetricsService returns the full entropy source at some point during
- // BrowserMain startup.
- ASSERT_TRUE(g_browser_process->metrics_service());
- EXPECT_EQ(MetricsService::LAST_ENTROPY_HIGH,
- g_browser_process->metrics_service()->entropy_source_returned());
-}
diff --git a/chrome/browser/metrics/metrics_service_unittest.cc b/chrome/browser/metrics/metrics_service_unittest.cc
index 48f7ec1..b42d789 100644
--- a/chrome/browser/metrics/metrics_service_unittest.cc
+++ b/chrome/browser/metrics/metrics_service_unittest.cc
@@ -4,7 +4,6 @@
#include "chrome/browser/metrics/metrics_service.h"
-#include <ctype.h>
#include <string>
#include "base/command_line.h"
@@ -97,6 +96,13 @@ class MetricsServiceTest : public testing::Test {
return testing_local_state_.Get();
}
+ // Sets metrics reporting as enabled for testing.
+ void EnableMetricsReporting() {
+ // TODO(asvitkine): Refactor the code to not need this flag and delete it.
+ CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kEnableMetricsReportingForTesting);
+ }
+
// Waits until base::TimeTicks::Now() no longer equals |value|. This should
// take between 1-15ms per the documented resolution of base::TimeTicks.
void WaitUntilTimeChanges(const base::TimeTicks& value) {
@@ -131,20 +137,6 @@ class MetricsServiceTest : public testing::Test {
} // namespace
-// Ensure the ClientId is formatted as expected.
-TEST_F(MetricsServiceTest, ClientIdCorrectlyFormatted) {
- std::string clientid = MetricsService::GenerateClientID();
- EXPECT_EQ(36U, clientid.length());
-
- for (size_t i = 0; i < clientid.length(); ++i) {
- char current = clientid[i];
- if (i == 8 || i == 13 || i == 18 || i == 23)
- EXPECT_EQ('-', current);
- else
- EXPECT_TRUE(isxdigit(current));
- }
-}
-
TEST_F(MetricsServiceTest, IsPluginProcess) {
EXPECT_TRUE(
MetricsService::IsPluginProcess(content::PROCESS_TYPE_PLUGIN));
@@ -154,82 +146,19 @@ TEST_F(MetricsServiceTest, IsPluginProcess) {
MetricsService::IsPluginProcess(content::PROCESS_TYPE_GPU));
}
-TEST_F(MetricsServiceTest, LowEntropySource0NotReset) {
- MetricsService service;
-
- // Get the low entropy source once, to initialize it.
- service.GetLowEntropySource();
-
- // Now, set it to 0 and ensure it doesn't get reset.
- service.low_entropy_source_ = 0;
- EXPECT_EQ(0, service.GetLowEntropySource());
- // Call it another time, just to make sure.
- EXPECT_EQ(0, service.GetLowEntropySource());
-}
-
-TEST_F(MetricsServiceTest, PermutedEntropyCacheClearedWhenLowEntropyReset) {
- const PrefService::Preference* low_entropy_pref =
- GetLocalState()->FindPreference(prefs::kMetricsLowEntropySource);
- const char* kCachePrefName = prefs::kMetricsPermutedEntropyCache;
- int low_entropy_value = -1;
-
- // First, generate an initial low entropy source value.
- {
- EXPECT_TRUE(low_entropy_pref->IsDefaultValue());
-
- MetricsService::SetExecutionPhase(MetricsService::UNINITIALIZED_PHASE);
- MetricsService service;
- service.GetLowEntropySource();
-
- EXPECT_FALSE(low_entropy_pref->IsDefaultValue());
- EXPECT_TRUE(low_entropy_pref->GetValue()->GetAsInteger(&low_entropy_value));
- }
-
- // Now, set a dummy value in the permuted entropy cache pref and verify that
- // another call to GetLowEntropySource() doesn't clobber it when
- // --reset-variation-state wasn't specified.
- {
- GetLocalState()->SetString(kCachePrefName, "test");
-
- MetricsService::SetExecutionPhase(MetricsService::UNINITIALIZED_PHASE);
- MetricsService service;
- service.GetLowEntropySource();
-
- EXPECT_EQ("test", GetLocalState()->GetString(kCachePrefName));
- EXPECT_EQ(low_entropy_value,
- GetLocalState()->GetInteger(prefs::kMetricsLowEntropySource));
- }
-
- // Verify that the cache does get reset if --reset-variations-state is passed.
- {
- CommandLine::ForCurrentProcess()->AppendSwitch(
- switches::kResetVariationState);
-
- MetricsService::SetExecutionPhase(MetricsService::UNINITIALIZED_PHASE);
- MetricsService service;
- service.GetLowEntropySource();
-
- EXPECT_TRUE(GetLocalState()->GetString(kCachePrefName).empty());
- }
-}
-
TEST_F(MetricsServiceTest, InitialStabilityLogAfterCleanShutDown) {
- base::FieldTrialList field_trial_list(NULL);
- base::FieldTrialList::CreateFieldTrial("UMAStability", "SeparateLog");
-
+ EnableMetricsReporting();
GetLocalState()->SetBoolean(prefs::kStabilityExitedCleanly, true);
TestMetricsService service;
- service.InitializeMetricsRecordingState(MetricsService::REPORTING_ENABLED);
+ service.InitializeMetricsRecordingState();
// No initial stability log should be generated.
EXPECT_FALSE(service.log_manager()->has_unsent_logs());
EXPECT_FALSE(service.log_manager()->has_staged_log());
}
TEST_F(MetricsServiceTest, InitialStabilityLogAfterCrash) {
- base::FieldTrialList field_trial_list(NULL);
- base::FieldTrialList::CreateFieldTrial("UMAStability", "SeparateLog");
-
+ EnableMetricsReporting();
GetLocalState()->ClearPref(prefs::kStabilityExitedCleanly);
// Set up prefs to simulate restarting after a crash.
@@ -251,7 +180,7 @@ TEST_F(MetricsServiceTest, InitialStabilityLogAfterCrash) {
GetLocalState()->SetBoolean(prefs::kStabilityExitedCleanly, false);
TestMetricsService service;
- service.InitializeMetricsRecordingState(MetricsService::REPORTING_ENABLED);
+ service.InitializeMetricsRecordingState();
// The initial stability log should be generated and persisted in unsent logs.
MetricsLogManager* log_manager = service.log_manager();
@@ -375,42 +304,3 @@ TEST_F(MetricsServiceTest, CrashReportingEnabled) {
EXPECT_FALSE(MetricsServiceHelper::IsCrashReportingEnabled());
#endif // defined(GOOGLE_CHROME_BUILD)
}
-
-// Check that setting the kMetricsResetIds pref to true causes the client id to
-// be reset. We do not check that the low entropy source is reset because we
-// cannot ensure that metrics service won't generate the same id again.
-TEST_F(MetricsServiceTest, ResetMetricsIDs) {
- // Set an initial client id in prefs. It should not be possible for the
- // metrics service to generate this id randomly.
- const std::string kInitialClientId = "initial client id";
- GetLocalState()->SetString(prefs::kMetricsClientID, kInitialClientId);
-
- // Make sure the initial client id isn't reset by the metrics service.
- {
- MetricsService service;
- service.ForceClientIdCreation();
- EXPECT_TRUE(service.metrics_ids_reset_check_performed_);
- EXPECT_EQ(kInitialClientId, service.client_id_);
- }
-
-
- // Set the reset pref to cause the IDs to be reset.
- GetLocalState()->SetBoolean(prefs::kMetricsResetIds, true);
-
- // Cause the actual reset to happen.
- {
- MetricsService service;
- service.ForceClientIdCreation();
- EXPECT_TRUE(service.metrics_ids_reset_check_performed_);
- EXPECT_NE(kInitialClientId, service.client_id_);
-
- service.GetLowEntropySource();
-
- EXPECT_FALSE(GetLocalState()->GetBoolean(prefs::kMetricsResetIds));
- }
-
- std::string new_client_id =
- GetLocalState()->GetString(prefs::kMetricsClientID);
-
- EXPECT_NE(kInitialClientId, new_client_id);
-}
diff --git a/chrome/browser/metrics/metrics_state_manager.cc b/chrome/browser/metrics/metrics_state_manager.cc
new file mode 100644
index 0000000..e72e6b7
--- /dev/null
+++ b/chrome/browser/metrics/metrics_state_manager.cc
@@ -0,0 +1,233 @@
+// Copyright 2014 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 "chrome/browser/metrics/metrics_state_manager.h"
+
+#include "base/command_line.h"
+#include "base/guid.h"
+#include "base/metrics/histogram.h"
+#include "base/metrics/sparse_histogram.h"
+#include "base/prefs/pref_registry_simple.h"
+#include "base/prefs/pref_service.h"
+#include "base/rand_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/time/time.h"
+#include "chrome/browser/metrics/cloned_install_detector.h"
+#include "chrome/browser/metrics/machine_id_provider.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/metrics/caching_permuted_entropy_provider.h"
+#include "chrome/common/pref_names.h"
+
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/settings/cros_settings.h"
+#endif
+
+namespace metrics {
+
+namespace {
+
+// The argument used to generate a non-identifying entropy source. We want no
+// more than 13 bits of entropy, so use this max to return a number in the range
+// [0, 7999] as the entropy source (12.97 bits of entropy).
+const int kMaxLowEntropySize = 8000;
+
+// Default prefs value for prefs::kMetricsLowEntropySource to indicate that the
+// value has not yet been set.
+const int kLowEntropySourceNotSet = -1;
+
+// Generates a new non-identifying entropy source used to seed persistent
+// activities.
+int GenerateLowEntropySource() {
+ return base::RandInt(0, kMaxLowEntropySize - 1);
+}
+
+} // namespace
+
+// static
+bool MetricsStateManager::instance_exists_ = false;
+
+MetricsStateManager::MetricsStateManager(PrefService* local_state)
+ : local_state_(local_state),
+ low_entropy_source_(kLowEntropySourceNotSet),
+ entropy_source_returned_(ENTROPY_SOURCE_NONE) {
+ ResetMetricsIDsIfNecessary();
+ if (IsMetricsReportingEnabled())
+ ForceClientIdCreation();
+
+ DCHECK(!instance_exists_);
+ instance_exists_ = true;
+}
+
+MetricsStateManager::~MetricsStateManager() {
+ DCHECK(instance_exists_);
+ instance_exists_ = false;
+}
+
+bool MetricsStateManager::IsMetricsReportingEnabled() {
+ // If the user permits metrics reporting with the checkbox in the
+ // prefs, we turn on recording. We disable metrics completely for
+ // non-official builds. This can be forced with a flag.
+ const CommandLine* command_line = CommandLine::ForCurrentProcess();
+ if (command_line->HasSwitch(switches::kEnableMetricsReportingForTesting))
+ return true;
+
+ // Disable metrics reporting when field trials are forced.
+ if (command_line->HasSwitch(switches::kForceFieldTrials))
+ return false;
+
+ bool enabled = false;
+#if defined(GOOGLE_CHROME_BUILD)
+#if defined(OS_CHROMEOS)
+ chromeos::CrosSettings::Get()->GetBoolean(chromeos::kStatsReportingPref,
+ &enabled);
+#else
+ enabled = local_state->GetBoolean(prefs::kMetricsReportingEnabled);
+#endif // #if defined(OS_CHROMEOS)
+#endif // defined(GOOGLE_CHROME_BUILD)
+ return enabled;
+}
+
+void MetricsStateManager::ForceClientIdCreation() {
+ if (!client_id_.empty())
+ return;
+
+ client_id_ = local_state_->GetString(prefs::kMetricsClientID);
+ if (!client_id_.empty())
+ return;
+
+ client_id_ = base::GenerateGUID();
+ local_state_->SetString(prefs::kMetricsClientID, client_id_);
+
+ if (local_state_->GetString(prefs::kMetricsOldClientID).empty()) {
+ // Record the timestamp of when the user opted in to UMA.
+ local_state_->SetInt64(prefs::kMetricsReportingEnabledTimestamp,
+ base::Time::Now().ToTimeT());
+ } else {
+ UMA_HISTOGRAM_BOOLEAN("UMA.ClientIdMigrated", true);
+ }
+ local_state_->ClearPref(prefs::kMetricsOldClientID);
+}
+
+void MetricsStateManager::CheckForClonedInstall() {
+ DCHECK(!cloned_install_detector_);
+
+ MachineIdProvider* provider = MachineIdProvider::CreateInstance();
+ if (!provider)
+ return;
+
+ cloned_install_detector_.reset(new ClonedInstallDetector(provider));
+ cloned_install_detector_->CheckForClonedInstall(local_state_);
+}
+
+scoped_ptr<const base::FieldTrial::EntropyProvider>
+MetricsStateManager::CreateEntropyProvider() {
+ // For metrics reporting-enabled users, we combine the client ID and low
+ // entropy source to get the final entropy source. Otherwise, only use the low
+ // entropy source.
+ // This has two useful properties:
+ // 1) It makes the entropy source less identifiable for parties that do not
+ // know the low entropy source.
+ // 2) It makes the final entropy source resettable.
+ const int low_entropy_source_value = GetLowEntropySource();
+ UMA_HISTOGRAM_SPARSE_SLOWLY("UMA.LowEntropySourceValue",
+ low_entropy_source_value);
+ if (IsMetricsReportingEnabled()) {
+ if (entropy_source_returned_ == ENTROPY_SOURCE_NONE)
+ entropy_source_returned_ = ENTROPY_SOURCE_HIGH;
+ DCHECK_EQ(ENTROPY_SOURCE_HIGH, entropy_source_returned_);
+ const std::string high_entropy_source =
+ client_id_ + base::IntToString(low_entropy_source_value);
+ return scoped_ptr<const base::FieldTrial::EntropyProvider>(
+ new SHA1EntropyProvider(high_entropy_source));
+ }
+
+ if (entropy_source_returned_ == ENTROPY_SOURCE_NONE)
+ entropy_source_returned_ = ENTROPY_SOURCE_LOW;
+ DCHECK_EQ(ENTROPY_SOURCE_LOW, entropy_source_returned_);
+
+#if defined(OS_ANDROID) || defined(OS_IOS)
+ return scoped_ptr<const base::FieldTrial::EntropyProvider>(
+ new CachingPermutedEntropyProvider(local_state_,
+ low_entropy_source_value,
+ kMaxLowEntropySize));
+#else
+ return scoped_ptr<const base::FieldTrial::EntropyProvider>(
+ new PermutedEntropyProvider(low_entropy_source_value,
+ kMaxLowEntropySize));
+#endif
+}
+
+// static
+scoped_ptr<MetricsStateManager> MetricsStateManager::Create(
+ PrefService* local_state) {
+ scoped_ptr<MetricsStateManager> result;
+ // Note: |instance_exists_| is updated in the constructor and destructor.
+ if (!instance_exists_)
+ result.reset(new MetricsStateManager(local_state));
+ return result.Pass();
+}
+
+// static
+void MetricsStateManager::RegisterPrefs(PrefRegistrySimple* registry) {
+ registry->RegisterBooleanPref(prefs::kMetricsResetIds, false);
+ registry->RegisterStringPref(prefs::kMetricsClientID, std::string());
+ registry->RegisterInt64Pref(prefs::kMetricsReportingEnabledTimestamp, 0);
+ registry->RegisterIntegerPref(prefs::kMetricsLowEntropySource,
+ kLowEntropySourceNotSet);
+
+ ClonedInstallDetector::RegisterPrefs(registry);
+ CachingPermutedEntropyProvider::RegisterPrefs(registry);
+
+ // TODO(asvitkine): Remove these once a couple of releases have passed.
+ // http://crbug.com/357704
+ registry->RegisterStringPref(prefs::kMetricsOldClientID, std::string());
+ registry->RegisterIntegerPref(prefs::kMetricsOldLowEntropySource, 0);
+}
+
+int MetricsStateManager::GetLowEntropySource() {
+ // Note that the default value for the low entropy source and the default pref
+ // value are both kLowEntropySourceNotSet, which is used to identify if the
+ // value has been set or not.
+ if (low_entropy_source_ != kLowEntropySourceNotSet)
+ return low_entropy_source_;
+
+ const CommandLine* command_line(CommandLine::ForCurrentProcess());
+ // Only try to load the value from prefs if the user did not request a reset.
+ // Otherwise, skip to generating a new value.
+ if (!command_line->HasSwitch(switches::kResetVariationState)) {
+ int value = local_state_->GetInteger(prefs::kMetricsLowEntropySource);
+ // If the value is outside the [0, kMaxLowEntropySize) range, re-generate
+ // it below.
+ if (value >= 0 && value < kMaxLowEntropySize) {
+ low_entropy_source_ = value;
+ UMA_HISTOGRAM_BOOLEAN("UMA.GeneratedLowEntropySource", false);
+ return low_entropy_source_;
+ }
+ }
+
+ UMA_HISTOGRAM_BOOLEAN("UMA.GeneratedLowEntropySource", true);
+ low_entropy_source_ = GenerateLowEntropySource();
+ local_state_->SetInteger(prefs::kMetricsLowEntropySource,
+ low_entropy_source_);
+ local_state_->ClearPref(prefs::kMetricsOldLowEntropySource);
+ metrics::CachingPermutedEntropyProvider::ClearCache(local_state_);
+
+ return low_entropy_source_;
+}
+
+void MetricsStateManager::ResetMetricsIDsIfNecessary() {
+ if (!local_state_->GetBoolean(prefs::kMetricsResetIds))
+ return;
+
+ UMA_HISTOGRAM_BOOLEAN("UMA.MetricsIDsReset", true);
+
+ DCHECK(client_id_.empty());
+ DCHECK_EQ(kLowEntropySourceNotSet, low_entropy_source_);
+
+ local_state_->ClearPref(prefs::kMetricsClientID);
+ local_state_->ClearPref(prefs::kMetricsLowEntropySource);
+ local_state_->ClearPref(prefs::kMetricsResetIds);
+}
+
+} // namespace metrics
diff --git a/chrome/browser/metrics/metrics_state_manager.h b/chrome/browser/metrics/metrics_state_manager.h
new file mode 100644
index 0000000..5b2f954
--- /dev/null
+++ b/chrome/browser/metrics/metrics_state_manager.h
@@ -0,0 +1,124 @@
+// Copyright 2014 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.
+
+#ifndef CHROME_BROWSER_METRICS_METRICS_STATE_MANAGER_H_
+#define CHROME_BROWSER_METRICS_METRICS_STATE_MANAGER_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/metrics/field_trial.h"
+
+class PrefService;
+class PrefRegistrySimple;
+
+namespace metrics {
+
+class ClonedInstallDetector;
+
+// Responsible for managing MetricsService state prefs, specifically the UMA
+// client id and low entropy source. Code outside the metrics directory should
+// not be instantiating or using this class directly.
+class MetricsStateManager {
+ public:
+ virtual ~MetricsStateManager();
+
+ // Returns true if the user opted in to sending metric reports.
+ // TODO(asvitkine): This function does not report the correct value on
+ // Android, see http://crbug.com/362192.
+ bool IsMetricsReportingEnabled();
+
+ // Returns the client ID for this client, or the empty string if the user is
+ // not opted in to metrics reporting.
+ const std::string& client_id() const { return client_id_; }
+
+ // Forces the client ID to be generated. This is useful in case it's needed
+ // before recording.
+ void ForceClientIdCreation();
+
+ // Checks if this install was cloned or imaged from another machine. If a
+ // clone is detected, resets the client id and low entropy source. This
+ // should not be called more than once.
+ void CheckForClonedInstall();
+
+ // Returns the preferred entropy provider used to seed persistent activities
+ // based on whether or not metrics reporting is permitted on this client.
+ //
+ // If metrics reporting is enabled, this method returns an entropy provider
+ // that has a high source of entropy, partially based on the client ID.
+ // Otherwise, it returns an entropy provider that is based on a low entropy
+ // source.
+ scoped_ptr<const base::FieldTrial::EntropyProvider> CreateEntropyProvider();
+
+ // Creates the MetricsStateManager, enforcing that only a single instance
+ // of the class exists at a time. Returns NULL if an instance exists already.
+ static scoped_ptr<MetricsStateManager> Create(PrefService* local_state);
+
+ // Registers local state prefs used by this class.
+ static void RegisterPrefs(PrefRegistrySimple* registry);
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(MetricsStateManagerTest, EntropySourceUsed_Low);
+ FRIEND_TEST_ALL_PREFIXES(MetricsStateManagerTest, EntropySourceUsed_High);
+ FRIEND_TEST_ALL_PREFIXES(MetricsStateManagerTest, LowEntropySource0NotReset);
+ FRIEND_TEST_ALL_PREFIXES(MetricsStateManagerTest,
+ PermutedEntropyCacheClearedWhenLowEntropyReset);
+ FRIEND_TEST_ALL_PREFIXES(MetricsStateManagerTest, ResetMetricsIDs);
+
+ // Designates which entropy source was returned from this class.
+ // This is used for testing to validate that we return the correct source
+ // depending on the state of the service.
+ enum EntropySourceType {
+ ENTROPY_SOURCE_NONE,
+ ENTROPY_SOURCE_LOW,
+ ENTROPY_SOURCE_HIGH,
+ };
+
+ // Creates the MetricsStateManager with the given |local_state|. Clients
+ // should instead use Create(), which enforces a single instance of this class
+ // is alive at any given time.
+ explicit MetricsStateManager(PrefService* local_state);
+
+ // Returns the low entropy source for this client. This is a random value
+ // that is non-identifying amongst browser clients. This method will
+ // generate the entropy source value if it has not been called before.
+ int GetLowEntropySource();
+
+ // Returns the first entropy source that was returned by this service since
+ // start up, or NONE if neither was returned yet. This is exposed for testing
+ // only.
+ EntropySourceType entropy_source_returned() const {
+ return entropy_source_returned_;
+ }
+
+ // Reset the client id and low entropy source if the kMetricsResetMetricIDs
+ // pref is true.
+ void ResetMetricsIDsIfNecessary();
+
+ // Whether an instance of this class exists. Used to enforce that there aren't
+ // multiple instances of this class at a given time.
+ static bool instance_exists_;
+
+ // Weak pointer to the local state prefs store.
+ PrefService* local_state_;
+
+ // The identifier that's sent to the server with the log reports.
+ std::string client_id_;
+
+ // The non-identifying low entropy source value.
+ int low_entropy_source_;
+
+ // The last entropy source returned by this service, used for testing.
+ EntropySourceType entropy_source_returned_;
+
+ scoped_ptr<ClonedInstallDetector> cloned_install_detector_;
+
+ DISALLOW_COPY_AND_ASSIGN(MetricsStateManager);
+};
+
+} // namespace metrics
+
+#endif // CHROME_BROWSER_METRICS_METRICS_STATE_MANAGER_H_
diff --git a/chrome/browser/metrics/metrics_state_manager_unittest.cc b/chrome/browser/metrics/metrics_state_manager_unittest.cc
new file mode 100644
index 0000000..bad4b77
--- /dev/null
+++ b/chrome/browser/metrics/metrics_state_manager_unittest.cc
@@ -0,0 +1,160 @@
+// Copyright 2014 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 "chrome/browser/metrics/metrics_state_manager.h"
+
+#include <ctype.h>
+#include <string>
+
+#include "base/command_line.h"
+#include "base/prefs/testing_pref_service.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/metrics/caching_permuted_entropy_provider.h"
+#include "chrome/common/pref_names.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace metrics {
+
+class MetricsStateManagerTest : public testing::Test {
+ public:
+ MetricsStateManagerTest() {
+ MetricsStateManager::RegisterPrefs(prefs_.registry());
+ }
+
+ scoped_ptr<MetricsStateManager> CreateStateManager() {
+ return MetricsStateManager::Create(&prefs_).Pass();
+ }
+
+ protected:
+ TestingPrefServiceSimple prefs_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MetricsStateManagerTest);
+};
+
+// Ensure the ClientId is formatted as expected.
+TEST_F(MetricsStateManagerTest, ClientIdCorrectlyFormatted) {
+ scoped_ptr<MetricsStateManager> state_manager(CreateStateManager());
+ state_manager->ForceClientIdCreation();
+
+ const std::string client_id = state_manager->client_id();
+ EXPECT_EQ(36U, client_id.length());
+
+ for (size_t i = 0; i < client_id.length(); ++i) {
+ char current = client_id[i];
+ if (i == 8 || i == 13 || i == 18 || i == 23)
+ EXPECT_EQ('-', current);
+ else
+ EXPECT_TRUE(isxdigit(current));
+ }
+}
+
+TEST_F(MetricsStateManagerTest, EntropySourceUsed_Low) {
+ scoped_ptr<MetricsStateManager> state_manager(CreateStateManager());
+ state_manager->CreateEntropyProvider();
+ EXPECT_EQ(MetricsStateManager::ENTROPY_SOURCE_LOW,
+ state_manager->entropy_source_returned());
+}
+
+TEST_F(MetricsStateManagerTest, EntropySourceUsed_High) {
+ CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kEnableMetricsReportingForTesting);
+
+ scoped_ptr<MetricsStateManager> state_manager(CreateStateManager());
+ state_manager->CreateEntropyProvider();
+ EXPECT_EQ(MetricsStateManager::ENTROPY_SOURCE_HIGH,
+ state_manager->entropy_source_returned());
+}
+
+TEST_F(MetricsStateManagerTest, LowEntropySource0NotReset) {
+ scoped_ptr<MetricsStateManager> state_manager(CreateStateManager());
+
+ // Get the low entropy source once, to initialize it.
+ state_manager->GetLowEntropySource();
+
+ // Now, set it to 0 and ensure it doesn't get reset.
+ state_manager->low_entropy_source_ = 0;
+ EXPECT_EQ(0, state_manager->GetLowEntropySource());
+ // Call it another time, just to make sure.
+ EXPECT_EQ(0, state_manager->GetLowEntropySource());
+}
+
+TEST_F(MetricsStateManagerTest,
+ PermutedEntropyCacheClearedWhenLowEntropyReset) {
+ const PrefService::Preference* low_entropy_pref =
+ prefs_.FindPreference(prefs::kMetricsLowEntropySource);
+ const char* kCachePrefName = prefs::kMetricsPermutedEntropyCache;
+ int low_entropy_value = -1;
+
+ // First, generate an initial low entropy source value.
+ {
+ EXPECT_TRUE(low_entropy_pref->IsDefaultValue());
+
+ scoped_ptr<MetricsStateManager> state_manager(CreateStateManager());
+ state_manager->GetLowEntropySource();
+
+ EXPECT_FALSE(low_entropy_pref->IsDefaultValue());
+ EXPECT_TRUE(low_entropy_pref->GetValue()->GetAsInteger(&low_entropy_value));
+ }
+
+ // Now, set a dummy value in the permuted entropy cache pref and verify that
+ // another call to GetLowEntropySource() doesn't clobber it when
+ // --reset-variation-state wasn't specified.
+ {
+ prefs_.SetString(kCachePrefName, "test");
+
+ scoped_ptr<MetricsStateManager> state_manager(CreateStateManager());
+ state_manager->GetLowEntropySource();
+
+ EXPECT_EQ("test", prefs_.GetString(kCachePrefName));
+ EXPECT_EQ(low_entropy_value,
+ prefs_.GetInteger(prefs::kMetricsLowEntropySource));
+ }
+
+ // Verify that the cache does get reset if --reset-variations-state is passed.
+ {
+ CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kResetVariationState);
+
+ scoped_ptr<MetricsStateManager> state_manager(CreateStateManager());
+ state_manager->GetLowEntropySource();
+
+ EXPECT_TRUE(prefs_.GetString(kCachePrefName).empty());
+ }
+}
+
+// Check that setting the kMetricsResetIds pref to true causes the client id to
+// be reset. We do not check that the low entropy source is reset because we
+// cannot ensure that metrics state manager won't generate the same id again.
+TEST_F(MetricsStateManagerTest, ResetMetricsIDs) {
+ // Set an initial client id in prefs. It should not be possible for the
+ // metrics state manager to generate this id randomly.
+ const std::string kInitialClientId = "initial client id";
+ prefs_.SetString(prefs::kMetricsClientID, kInitialClientId);
+
+ // Make sure the initial client id isn't reset by the metrics state manager.
+ {
+ scoped_ptr<MetricsStateManager> state_manager(CreateStateManager());
+ state_manager->ForceClientIdCreation();
+ EXPECT_EQ(kInitialClientId, state_manager->client_id());
+ }
+
+ // Set the reset pref to cause the IDs to be reset.
+ prefs_.SetBoolean(prefs::kMetricsResetIds, true);
+
+ // Cause the actual reset to happen.
+ {
+ scoped_ptr<MetricsStateManager> state_manager(CreateStateManager());
+ state_manager->ForceClientIdCreation();
+ EXPECT_NE(kInitialClientId, state_manager->client_id());
+
+ state_manager->GetLowEntropySource();
+
+ EXPECT_FALSE(prefs_.GetBoolean(prefs::kMetricsResetIds));
+ }
+
+ EXPECT_NE(kInitialClientId, prefs_.GetString(prefs::kMetricsClientID));
+}
+
+} // namespace metrics
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index e256661..28fa463 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -37,7 +37,6 @@
#include "chrome/browser/media/media_capture_devices_dispatcher.h"
#include "chrome/browser/media/media_device_id_salt.h"
#include "chrome/browser/media/media_stream_devices_controller.h"
-#include "chrome/browser/metrics/cloned_install_detector.h"
#include "chrome/browser/metrics/metrics_log.h"
#include "chrome/browser/metrics/metrics_service.h"
#include "chrome/browser/metrics/variations/variations_service.h"
@@ -87,7 +86,6 @@
#include "chrome/browser/ui/webui/print_preview/sticky_settings.h"
#include "chrome/browser/upgrade_detector.h"
#include "chrome/browser/web_resource/promo_resource_service.h"
-#include "chrome/common/metrics/caching_permuted_entropy_provider.h"
#include "chrome/common/pref_names.h"
#include "components/autofill/core/browser/autofill_manager.h"
#include "components/bookmarks/core/browser/bookmark_utils.h"
@@ -235,8 +233,6 @@ void RegisterLocalState(PrefRegistrySimple* registry) {
KeywordEditorController::RegisterPrefs(registry);
MetricsLog::RegisterPrefs(registry);
MetricsService::RegisterPrefs(registry);
- metrics::CachingPermutedEntropyProvider::RegisterPrefs(registry);
- metrics::ClonedInstallDetector::RegisterPrefs(registry);
PrefProxyConfigTrackerImpl::RegisterPrefs(registry);
ProfileInfoCache::RegisterPrefs(registry);
profiles::RegisterPrefs(registry);
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 3004f9f..debae30 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -1215,6 +1215,8 @@
'browser/metrics/metrics_service_android.cc',
'browser/metrics/metrics_service.cc',
'browser/metrics/metrics_service.h',
+ 'browser/metrics/metrics_state_manager.cc',
+ 'browser/metrics/metrics_state_manager.h',
'browser/metrics/perf_provider_chromeos.cc',
'browser/metrics/perf_provider_chromeos.h',
'browser/metrics/thread_watcher.cc',
diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi
index 8b98fa2..680772a 100644
--- a/chrome/chrome_tests_unit.gypi
+++ b/chrome/chrome_tests_unit.gypi
@@ -1084,6 +1084,7 @@
'browser/metrics/metrics_log_serializer_unittest.cc',
'browser/metrics/metrics_reporting_scheduler_unittest.cc',
'browser/metrics/metrics_service_unittest.cc',
+ 'browser/metrics/metrics_state_manager_unittest.cc',
'browser/metrics/thread_watcher_unittest.cc',
'browser/metrics/thread_watcher_android_unittest.cc',
'browser/metrics/time_ticks_experiment_unittest.cc',