Index: trunk/src/chrome/browser/metrics/metrics_service.cc |
=================================================================== |
--- trunk/src/chrome/browser/metrics/metrics_service.cc (revision 268245) |
+++ trunk/src/chrome/browser/metrics/metrics_service.cc (working copy) |
@@ -168,12 +168,14 @@ |
#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" |
@@ -185,11 +187,12 @@ |
#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" |
@@ -203,6 +206,7 @@ |
#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" |
@@ -309,6 +313,21 @@ |
} |
} |
+// 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) { |
@@ -411,8 +430,11 @@ |
// static |
void MetricsService::RegisterPrefs(PrefRegistrySimple* registry) { |
DCHECK(IsSingleThreaded()); |
- metrics::MetricsStateManager::RegisterPrefs(registry); |
- |
+ registry->RegisterBooleanPref(prefs::kMetricsResetIds, false); |
+ registry->RegisterStringPref(prefs::kMetricsClientID, std::string()); |
+ registry->RegisterInt64Pref(prefs::kMetricsReportingEnabledTimestamp, 0); |
+ registry->RegisterIntegerPref(prefs::kMetricsLowEntropySource, |
+ kLowEntropySourceNotSet); |
registry->RegisterInt64Pref(prefs::kStabilityLaunchTimeSec, 0); |
registry->RegisterInt64Pref(prefs::kStabilityLastTimestampSec, 0); |
registry->RegisterStringPref(prefs::kStabilityStatsVersion, std::string()); |
@@ -457,28 +479,33 @@ |
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() |
- : state_manager_(metrics::MetricsStateManager::Create( |
- g_browser_process->local_state())), |
+ : metrics_ids_reset_check_performed_(false), |
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) { |
+ num_async_histogram_fetches_in_progress_(0), |
+ entropy_source_returned_(LAST_ENTROPY_NONE) { |
DCHECK(IsSingleThreaded()); |
- DCHECK(state_manager_); |
log_manager_.set_log_serializer(new MetricsLogSerializer); |
log_manager_.set_max_ongoing_log_store_size(kUploadLogAvoidRetransmitSize); |
@@ -492,8 +519,9 @@ |
BrowserChildProcessObserver::Remove(this); |
} |
-void MetricsService::InitializeMetricsRecordingState() { |
- InitializeMetricsState(); |
+void MetricsService::InitializeMetricsRecordingState( |
+ ReportingState reporting_state) { |
+ InitializeMetricsState(reporting_state); |
base::Closure callback = base::Bind(&MetricsService::StartScheduledUpload, |
self_ptr_factory_.GetWeakPtr()); |
@@ -506,13 +534,6 @@ |
EnableReporting(); |
} |
-bool MetricsService::StartIfMetricsReportingEnabled() { |
- const bool enabled = state_manager_->IsMetricsReportingEnabled(); |
- if (enabled) |
- Start(); |
- return enabled; |
-} |
- |
void MetricsService::StartRecordingForTests() { |
test_mode_active_ = true; |
EnableRecording(); |
@@ -537,16 +558,72 @@ |
} |
std::string MetricsService::GetClientId() { |
- return state_manager_->client_id(); |
+ return client_id_; |
} |
scoped_ptr<const base::FieldTrial::EntropyProvider> |
-MetricsService::CreateEntropyProvider() { |
- // TODO(asvitkine): Refactor the code so that MetricsService does not expose |
- // this method. |
- return state_manager_->CreateEntropyProvider(); |
+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); |
+} |
+ |
void MetricsService::EnableRecording() { |
DCHECK(IsSingleThreaded()); |
@@ -554,8 +631,8 @@ |
return; |
recording_active_ = true; |
- state_manager_->ForceClientIdCreation(); |
- crash_keys::SetClientID(state_manager_->client_id()); |
+ ForceClientIdCreation(); |
+ crash_keys::SetClientID(client_id_); |
if (!log_manager_.current_log()) |
OpenNewLog(); |
@@ -841,7 +918,7 @@ |
//------------------------------------------------------------------------------ |
// Initialization methods |
-void MetricsService::InitializeMetricsState() { |
+void MetricsService::InitializeMetricsState(ReportingState reporting_state) { |
#if defined(OS_POSIX) |
network_stats_server_ = chrome_common_net::kEchoTestServerLocation; |
http_pipelining_test_server_ = chrome_common_net::kPipelineTestServerBaseUrl; |
@@ -878,7 +955,7 @@ |
// If the previous session didn't exit cleanly, then prepare an initial |
// stability log if UMA is enabled. |
- if (state_manager_->IsMetricsReportingEnabled()) |
+ if (reporting_state == REPORTING_ENABLED) |
PrepareInitialStabilityLog(); |
} |
@@ -1042,8 +1119,7 @@ |
// save the profiler data. |
if (!initial_metrics_log_.get()) { |
initial_metrics_log_.reset( |
- new MetricsLog(state_manager_->client_id(), session_id_, |
- MetricsLog::ONGOING_LOG)); |
+ new MetricsLog(client_id_, session_id_, MetricsLog::ONGOING_LOG)); |
} |
initial_metrics_log_->RecordProfilerData(process_data, process_type); |
@@ -1077,6 +1153,64 @@ |
} |
} |
+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 |
@@ -1110,8 +1244,7 @@ |
DCHECK(!log_manager_.current_log()); |
log_manager_.BeginLoggingWithLog( |
- new MetricsLog(state_manager_->client_id(), session_id_, |
- MetricsLog::ONGOING_LOG)); |
+ new MetricsLog(client_id_, session_id_, MetricsLog::ONGOING_LOG)); |
if (state_ == INITIALIZED) { |
// We only need to schedule that run once. |
state_ = INIT_TASK_SCHEDULED; |
@@ -1407,7 +1540,7 @@ |
DCHECK_NE(0, pref->GetInteger(prefs::kStabilityCrashCount)); |
scoped_ptr<MetricsLog> initial_stability_log( |
- new MetricsLog(state_manager_->client_id(), session_id_, |
+ new MetricsLog(client_id_, session_id_, |
MetricsLog::INITIAL_STABILITY_LOG)); |
if (!initial_stability_log->LoadSavedEnvironmentFromPrefs()) |
return; |
@@ -1706,7 +1839,18 @@ |
} |
void MetricsService::CheckForClonedInstall() { |
- state_manager_->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); |
} |
void MetricsService::GetCurrentSyntheticFieldTrials( |