Commit 37d4709a authored by jwd@chromium.org's avatar jwd@chromium.org

Resetting metrics ids on clump detection.

When a cloned install is detected the metrics client id and low entropy source are reset on the next start of Chrome. This is done by setting a pref in local state. The next time either the client id or low entropy source are loaded from the local state, they are first reset.

BUG=343273
NOTRY=true

Review URL: https://codereview.chromium.org/213363004

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@260348 0039d316-1c4b-4281-b951-d872f2087c98
parent 6e067c3e
...@@ -72,8 +72,14 @@ void ClonedInstallDetector::SaveMachineId( ...@@ -72,8 +72,14 @@ void ClonedInstallDetector::SaveMachineId(
MachineIdState id_state = ID_NO_STORED_VALUE; MachineIdState id_state = ID_NO_STORED_VALUE;
if (local_state->HasPrefPath(prefs::kMetricsMachineId)) { if (local_state->HasPrefPath(prefs::kMetricsMachineId)) {
id_state = local_state->GetInteger(prefs::kMetricsMachineId) == hashed_id ? if (local_state->GetInteger(prefs::kMetricsMachineId) != hashed_id) {
ID_UNCHANGED : ID_CHANGED; id_state = ID_CHANGED;
// TODO(jwd): Use a callback to set the reset pref. That way
// ClonedInstallDetector doesn't need to know about this pref.
local_state->SetBoolean(prefs::kMetricsResetIds, true);
} else {
id_state = ID_UNCHANGED;
}
} }
LogMachineIdState(id_state); LogMachineIdState(id_state);
......
...@@ -34,6 +34,7 @@ class ClonedInstallDetector { ...@@ -34,6 +34,7 @@ class ClonedInstallDetector {
private: private:
FRIEND_TEST_ALL_PREFIXES(ClonedInstallDetectorTest, SaveId); FRIEND_TEST_ALL_PREFIXES(ClonedInstallDetectorTest, SaveId);
FRIEND_TEST_ALL_PREFIXES(ClonedInstallDetectorTest, DetectClone);
// Converts raw_id into a 24-bit hash and stores the hash in |local_state|. // Converts raw_id into a 24-bit hash and stores the hash in |local_state|.
// |raw_id| is not a const ref because it's passed from a cross-thread post // |raw_id| is not a const ref because it's passed from a cross-thread post
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include "base/prefs/testing_pref_service.h" #include "base/prefs/testing_pref_service.h"
#include "chrome/browser/metrics/machine_id_provider.h" #include "chrome/browser/metrics/machine_id_provider.h"
#include "chrome/browser/metrics/metrics_service.h"
#include "chrome/common/pref_names.h" #include "chrome/common/pref_names.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
...@@ -19,7 +20,7 @@ const int kTestHashedId = 2216819; ...@@ -19,7 +20,7 @@ const int kTestHashedId = 2216819;
} // namespace } // namespace
// TODO(jwd): Change this test to test the full flow and histogram outputs. It // TODO(jwd): Change these test to test the full flow and histogram outputs. It
// should also remove the need to make the test a friend of // should also remove the need to make the test a friend of
// ClonedInstallDetector. // ClonedInstallDetector.
TEST(ClonedInstallDetectorTest, SaveId) { TEST(ClonedInstallDetectorTest, SaveId) {
...@@ -34,4 +35,20 @@ TEST(ClonedInstallDetectorTest, SaveId) { ...@@ -34,4 +35,20 @@ TEST(ClonedInstallDetectorTest, SaveId) {
EXPECT_EQ(kTestHashedId, prefs.GetInteger(prefs::kMetricsMachineId)); EXPECT_EQ(kTestHashedId, prefs.GetInteger(prefs::kMetricsMachineId));
} }
TEST(ClonedInstallDetectorTest, DetectClone) {
TestingPrefServiceSimple prefs;
ClonedInstallDetector::RegisterPrefs(prefs.registry());
MetricsService::RegisterPrefs(prefs.registry());
// Save a machine id that will cause a clone to be detected.
prefs.SetInteger(prefs::kMetricsMachineId, kTestHashedId + 1);
scoped_ptr<ClonedInstallDetector> detector(
new ClonedInstallDetector(MachineIdProvider::CreateInstance()));
detector->SaveMachineId(&prefs, kTestRawId);
EXPECT_TRUE(prefs.GetBoolean(prefs::kMetricsResetIds));
}
} // namespace metrics } // namespace metrics
...@@ -435,6 +435,7 @@ class MetricsMemoryDetails : public MemoryDetails { ...@@ -435,6 +435,7 @@ class MetricsMemoryDetails : public MemoryDetails {
// static // static
void MetricsService::RegisterPrefs(PrefRegistrySimple* registry) { void MetricsService::RegisterPrefs(PrefRegistrySimple* registry) {
DCHECK(IsSingleThreaded()); DCHECK(IsSingleThreaded());
registry->RegisterBooleanPref(prefs::kMetricsResetIds, false);
registry->RegisterStringPref(prefs::kMetricsClientID, std::string()); registry->RegisterStringPref(prefs::kMetricsClientID, std::string());
registry->RegisterIntegerPref(prefs::kMetricsLowEntropySource, registry->RegisterIntegerPref(prefs::kMetricsLowEntropySource,
kLowEntropySourceNotSet); kLowEntropySourceNotSet);
...@@ -521,7 +522,8 @@ void MetricsService::DiscardOldStabilityStats(PrefService* local_state) { ...@@ -521,7 +522,8 @@ void MetricsService::DiscardOldStabilityStats(PrefService* local_state) {
} }
MetricsService::MetricsService() MetricsService::MetricsService()
: recording_active_(false), : metrics_ids_reset_check_performed_(false),
recording_active_(false),
reporting_active_(false), reporting_active_(false),
test_mode_active_(false), test_mode_active_(false),
state_(INITIALIZED), state_(INITIALIZED),
...@@ -633,6 +635,9 @@ MetricsService::CreateEntropyProvider(ReportingState reporting_state) { ...@@ -633,6 +635,9 @@ MetricsService::CreateEntropyProvider(ReportingState reporting_state) {
void MetricsService::ForceClientIdCreation() { void MetricsService::ForceClientIdCreation() {
if (!client_id_.empty()) if (!client_id_.empty())
return; return;
ResetMetricsIDsIfNecessary();
PrefService* pref = g_browser_process->local_state(); PrefService* pref = g_browser_process->local_state();
client_id_ = pref->GetString(prefs::kMetricsClientID); client_id_ = pref->GetString(prefs::kMetricsClientID);
if (!client_id_.empty()) if (!client_id_.empty())
...@@ -1185,6 +1190,26 @@ void MetricsService::GetUptimes(PrefService* pref, ...@@ -1185,6 +1190,26 @@ 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() { int MetricsService::GetLowEntropySource() {
// Note that the default value for the low entropy source and the default pref // 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 are both kLowEntropySourceNotSet, which is used to identify if the
...@@ -1192,6 +1217,8 @@ int MetricsService::GetLowEntropySource() { ...@@ -1192,6 +1217,8 @@ int MetricsService::GetLowEntropySource() {
if (low_entropy_source_ != kLowEntropySourceNotSet) if (low_entropy_source_ != kLowEntropySourceNotSet)
return low_entropy_source_; return low_entropy_source_;
ResetMetricsIDsIfNecessary();
PrefService* local_state = g_browser_process->local_state(); PrefService* local_state = g_browser_process->local_state();
const CommandLine* command_line(CommandLine::ForCurrentProcess()); const CommandLine* command_line(CommandLine::ForCurrentProcess());
// Only try to load the value from prefs if the user did not request a reset. // Only try to load the value from prefs if the user did not request a reset.
......
...@@ -368,6 +368,10 @@ class MetricsService ...@@ -368,6 +368,10 @@ class MetricsService
base::TimeDelta* incremental_uptime, base::TimeDelta* incremental_uptime,
base::TimeDelta* 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 // Returns the low entropy source for this client. This is a random value
// that is non-identifying amongst browser clients. This method will // that is non-identifying amongst browser clients. This method will
// generate the entropy source value if it has not been called before. // generate the entropy source value if it has not been called before.
...@@ -523,6 +527,10 @@ class MetricsService ...@@ -523,6 +527,10 @@ class MetricsService
content::NotificationRegistrar registrar_; content::NotificationRegistrar registrar_;
// Set to true when |ResetMetricsIDsIfNecessary| is called for the first time.
// This prevents multiple resets within the same Chrome session.
bool metrics_ids_reset_check_performed_;
// Indicate whether recording and reporting are currently happening. // Indicate whether recording and reporting are currently happening.
// These should not be set directly, but by calling SetRecording and // These should not be set directly, but by calling SetRecording and
// SetReporting. // SetReporting.
...@@ -639,6 +647,7 @@ class MetricsService ...@@ -639,6 +647,7 @@ class MetricsService
FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest,
PermutedEntropyCacheClearedWhenLowEntropyReset); PermutedEntropyCacheClearedWhenLowEntropyReset);
FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, RegisterSyntheticTrial); FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, RegisterSyntheticTrial);
FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, ResetMetricsIDs);
FRIEND_TEST_ALL_PREFIXES(MetricsServiceBrowserTest, FRIEND_TEST_ALL_PREFIXES(MetricsServiceBrowserTest,
CheckLowEntropySourceUsed); CheckLowEntropySourceUsed);
FRIEND_TEST_ALL_PREFIXES(MetricsServiceReportingTest, FRIEND_TEST_ALL_PREFIXES(MetricsServiceReportingTest,
......
...@@ -374,3 +374,42 @@ TEST_F(MetricsServiceTest, CrashReportingEnabled) { ...@@ -374,3 +374,42 @@ TEST_F(MetricsServiceTest, CrashReportingEnabled) {
EXPECT_FALSE(MetricsServiceHelper::IsCrashReportingEnabled()); EXPECT_FALSE(MetricsServiceHelper::IsCrashReportingEnabled());
#endif // defined(GOOGLE_CHROME_BUILD) #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);
}
...@@ -1383,6 +1383,11 @@ const char kMetricsReportingEnabled[] = ...@@ -1383,6 +1383,11 @@ const char kMetricsReportingEnabled[] =
// stored locally and never transmitted in metrics reports. // stored locally and never transmitted in metrics reports.
const char kMetricsMachineId[] = "user_experience_metrics.machine_id"; const char kMetricsMachineId[] = "user_experience_metrics.machine_id";
// Boolean that indicates a cloned install has been detected and the metrics
// client id and low entropy source should be reset.
const char kMetricsResetIds[] =
"user_experience_metrics.reset_metrics_ids";
// Boolean that specifies whether or not crash reports are sent // Boolean that specifies whether or not crash reports are sent
// over the network for analysis. // over the network for analysis.
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
......
...@@ -438,6 +438,7 @@ extern const char kMetricsPermutedEntropyCache[]; ...@@ -438,6 +438,7 @@ extern const char kMetricsPermutedEntropyCache[];
extern const char kMetricsClientIDTimestamp[]; extern const char kMetricsClientIDTimestamp[];
extern const char kMetricsReportingEnabled[]; extern const char kMetricsReportingEnabled[];
extern const char kMetricsMachineId[]; extern const char kMetricsMachineId[];
extern const char kMetricsResetIds[];
// Android has it's own metric / crash reporting implemented in Android // Android has it's own metric / crash reporting implemented in Android
// Java code so kMetricsReportingEnabled doesn't make sense. We use this // Java code so kMetricsReportingEnabled doesn't make sense. We use this
// to inform crashes_ui that we have enabled crash reporting. // to inform crashes_ui that we have enabled crash reporting.
......
...@@ -27796,12 +27796,21 @@ other types of suffix sets. ...@@ -27796,12 +27796,21 @@ other types of suffix sets.
</histogram> </histogram>
<histogram name="UMA.MachineIdState" enum="UmaMachineIdState"> <histogram name="UMA.MachineIdState" enum="UmaMachineIdState">
<sumary> <owner>jwd@chromium.org</owner>
<summary>
Tracks if the machine ID is generated successfully and if it changes from Tracks if the machine ID is generated successfully and if it changes from
one run to the next. The machine ID is a 24-bit hash of machine one run to the next. The machine ID is a 24-bit hash of machine
characteristics. It is expected to change if an install of Chrome is copied characteristics. It is expected to change if an install of Chrome is copied
to multiple machines. This check happens once per browser startup. to multiple machines. This check happens once per browser startup.
</sumary> </summary>
</histogram>
<histogram name="UMA.MetricsIDsReset" enum="BooleanHit">
<owner>jwd@chromium.org</owner>
<summary>
A count of the number of times the metrics ids (client id and low entropy
source) have been reset due to a cloned install being detected.
</summary>
</histogram> </histogram>
<histogram name="UMA.Perf.GetData" enum="GetPerfDataOutcome"> <histogram name="UMA.Perf.GetData" enum="GetPerfDataOutcome">
...@@ -28137,7 +28146,7 @@ other types of suffix sets. ...@@ -28137,7 +28146,7 @@ other types of suffix sets.
</histogram> </histogram>
<histogram name="Variations.SeedDateChange" enum="VariationsSeedDateChange"> <histogram name="Variations.SeedDateChange" enum="VariationsSeedDateChange">
<owner>Please list the metric's owners. Add more owner tags as needed.</owner> <owner>jwd@chromium.org</owner>
<summary> <summary>
Counts if a response from the variations server is the first response of the Counts if a response from the variations server is the first response of the
day or not. This is counted when a new valid seed or a 304 is received. The day or not. This is counted when a new valid seed or a 304 is received. The
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment