Retrieve client_id from GoogleUpdateSettings when its missing from Local State.
Precursor refactoring CL @ https://codereview.chromium.org/365133005/
Precursor kInstallDate move to metrics_pref_names @ https://codereview.chromium.org/370813003/
This is the Windows implementation, POSIX implementation to follow in https://codereview.chromium.org/377713002/ (easier to develop and test on Linux in a follow-up CL)
BUG=391338
Review URL: https://codereview.chromium.org/372473004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@284805 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/app/DEPS b/chrome/app/DEPS
index ba43711..dcc07692 100644
--- a/chrome/app/DEPS
+++ b/chrome/app/DEPS
@@ -10,6 +10,7 @@
"+chromeos/chromeos_switches.h",
"+components/breakpad",
"+components/component_updater",
+ "+components/metrics/client_info.h",
"+components/nacl/common",
"+components/nacl/zygote",
"+components/startup_metric_utils",
diff --git a/chrome/app/client_util.cc b/chrome/app/client_util.cc
index ac6dbfc..efb79d5 100644
--- a/chrome/app/client_util.cc
+++ b/chrome/app/client_util.cc
@@ -34,6 +34,7 @@
#include "chrome/installer/util/util_constants.h"
#include "components/breakpad/app/breakpad_client.h"
#include "components/breakpad/app/breakpad_win.h"
+#include "components/metrics/client_info.h"
#include "content/public/app/startup_helper_win.h"
#include "sandbox/win/src/sandbox.h"
@@ -75,14 +76,14 @@
void GetPreReadPopulationAndGroup(double* population, double* group) {
// By default we use the metrics id for the user as stable pseudo-random
// input to a hash.
- std::string metrics_id;
- GoogleUpdateSettings::LoadMetricsClientId(&metrics_id);
+ scoped_ptr<metrics::ClientInfo> client_info =
+ GoogleUpdateSettings::LoadMetricsClientInfo();
- // If this user has not metrics id, we fall back to a purely random value
- // per browser session.
+ // If this user has no metrics id, we fall back to a purely random value per
+ // browser session.
const size_t kLength = 16;
- std::string random_value(metrics_id.empty() ? base::RandBytesAsString(kLength)
- : metrics_id);
+ std::string random_value(client_info ? client_info->client_id
+ : base::RandBytesAsString(kLength));
// To interpret the value as a random number we hash it and read the first 8
// bytes of the hash as a unit-interval representing a die-toss for being in
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index f534542..f9e7a72 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -101,6 +101,7 @@
#include "components/cdm/browser/cdm_message_filter_android.h"
#include "components/cloud_devices/common/cloud_devices_switches.h"
#include "components/google/core/browser/google_util.h"
+#include "components/metrics/client_info.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/signin/core/common/profile_management_switches.h"
#include "components/translate/core/common/translate_switches.h"
@@ -1485,10 +1486,11 @@
CommandLine* command_line, int child_process_id) {
#if defined(OS_POSIX)
if (breakpad::IsCrashReporterEnabled()) {
- std::string enable_crash_reporter;
- GoogleUpdateSettings::LoadMetricsClientId(&enable_crash_reporter);
+ scoped_ptr<metrics::ClientInfo> client_info =
+ GoogleUpdateSettings::LoadMetricsClientInfo();
command_line->AppendSwitchASCII(switches::kEnableCrashReporter,
- enable_crash_reporter);
+ client_info ? client_info->client_id
+ : std::string());
}
#endif // defined(OS_POSIX)
diff --git a/chrome/browser/google/google_update_settings_posix.cc b/chrome/browser/google/google_update_settings_posix.cc
index 99dc0cc..e6dad5e 100644
--- a/chrome/browser/google/google_update_settings_posix.cc
+++ b/chrome/browser/google/google_update_settings_posix.cc
@@ -67,24 +67,32 @@
}
// static
-bool GoogleUpdateSettings::LoadMetricsClientId(std::string* metrics_id) {
+// TODO(gab): Implement storing/loading for all ClientInfo fields on POSIX.
+scoped_ptr<metrics::ClientInfo> GoogleUpdateSettings::LoadMetricsClientInfo() {
+ scoped_ptr<metrics::ClientInfo> client_info(new metrics::ClientInfo);
+
base::AutoLock lock(g_posix_client_id_lock.Get());
- *metrics_id = g_posix_client_id.Get();
- return true;
+ if (g_posix_client_id.Get().empty())
+ return scoped_ptr<metrics::ClientInfo>();
+ client_info->client_id = g_posix_client_id.Get();
+
+ return client_info.Pass();
}
// static
-bool GoogleUpdateSettings::StoreMetricsClientId(const std::string& client_id) {
+// TODO(gab): Implement storing/loading for all ClientInfo fields on POSIX.
+void GoogleUpdateSettings::StoreMetricsClientInfo(
+ const metrics::ClientInfo& client_info) {
// Make sure that user has consented to send crashes.
if (!GoogleUpdateSettings::GetCollectStatsConsent())
- return false;
+ return;
{
// Since user has consented, write the metrics id to the file.
base::AutoLock lock(g_posix_client_id_lock.Get());
- g_posix_client_id.Get() = client_id;
+ g_posix_client_id.Get() = client_info.client_id;
}
- return GoogleUpdateSettings::SetCollectStatsConsent(true);
+ GoogleUpdateSettings::SetCollectStatsConsent(true);
}
// GetLastRunTime and SetLastRunTime are not implemented for posix. Their
diff --git a/chrome/browser/metrics/chrome_metrics_service_client.cc b/chrome/browser/metrics/chrome_metrics_service_client.cc
index f10cc15..def8cd6 100644
--- a/chrome/browser/metrics/chrome_metrics_service_client.cc
+++ b/chrome/browser/metrics/chrome_metrics_service_client.cc
@@ -35,7 +35,6 @@
#include "chrome/common/crash_keys.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/render_messages.h"
-#include "chrome/installer/util/google_update_settings.h"
#include "components/metrics/metrics_service.h"
#include "components/metrics/net/net_metrics_log_uploader.h"
#include "content/public/browser/browser_thread.h"
@@ -165,9 +164,6 @@
void ChromeMetricsServiceClient::SetMetricsClientId(
const std::string& client_id) {
crash_keys::SetCrashClientIdFromGUID(client_id);
-
- // Store a backup of the client id in Google Update settings.
- GoogleUpdateSettings::StoreMetricsClientId(client_id);
}
bool ChromeMetricsServiceClient::IsOffTheRecordSessionActive() {
diff --git a/chrome/browser/metrics/extensions_metrics_provider_unittest.cc b/chrome/browser/metrics/extensions_metrics_provider_unittest.cc
index f0fc87e..b18f6d9 100644
--- a/chrome/browser/metrics/extensions_metrics_provider_unittest.cc
+++ b/chrome/browser/metrics/extensions_metrics_provider_unittest.cc
@@ -6,7 +6,10 @@
#include <string>
+#include "base/memory/scoped_ptr.h"
#include "base/prefs/testing_pref_service.h"
+#include "components/metrics/client_info.h"
+#include "components/metrics/metrics_service.h"
#include "components/metrics/metrics_state_manager.h"
#include "components/metrics/proto/system_profile.pb.h"
#include "extensions/common/extension.h"
@@ -20,6 +23,13 @@
return true;
}
+void StoreNoClientInfoBackup(const metrics::ClientInfo& /* client_info */) {
+}
+
+scoped_ptr<metrics::ClientInfo> ReturnNoBackup() {
+ return scoped_ptr<metrics::ClientInfo>();
+}
+
class TestExtensionsMetricsProvider : public ExtensionsMetricsProvider {
public:
explicit TestExtensionsMetricsProvider(
@@ -93,10 +103,13 @@
TEST(ExtensionsMetricsProvider, SystemProtoEncoding) {
metrics::SystemProfileProto system_profile;
TestingPrefServiceSimple local_state;
- metrics::MetricsStateManager::RegisterPrefs(local_state.registry());
+ MetricsService::RegisterPrefs(local_state.registry());
scoped_ptr<metrics::MetricsStateManager> metrics_state_manager(
- metrics::MetricsStateManager::Create(&local_state,
- base::Bind(&IsMetricsReportingEnabled)));
+ metrics::MetricsStateManager::Create(
+ &local_state,
+ base::Bind(&IsMetricsReportingEnabled),
+ base::Bind(&StoreNoClientInfoBackup),
+ base::Bind(&ReturnNoBackup)));
TestExtensionsMetricsProvider extension_metrics(metrics_state_manager.get());
extension_metrics.ProvideSystemProfileMetrics(&system_profile);
ASSERT_EQ(2, system_profile.occupied_extension_bucket_size());
diff --git a/chrome/browser/metrics/metrics_services_manager.cc b/chrome/browser/metrics/metrics_services_manager.cc
index 8278913..74d3710 100644
--- a/chrome/browser/metrics/metrics_services_manager.cc
+++ b/chrome/browser/metrics/metrics_services_manager.cc
@@ -4,12 +4,16 @@
#include "chrome/browser/metrics/metrics_services_manager.h"
+#include <string>
+
#include "base/command_line.h"
+#include "base/logging.h"
#include "base/prefs/pref_service.h"
#include "chrome/browser/metrics/chrome_metrics_service_client.h"
#include "chrome/browser/metrics/variations/variations_service.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
+#include "chrome/installer/util/google_update_settings.h"
#include "components/metrics/metrics_service.h"
#include "components/metrics/metrics_state_manager.h"
#include "components/rappor/rappor_service.h"
@@ -70,7 +74,9 @@
metrics_state_manager_ = metrics::MetricsStateManager::Create(
local_state_,
base::Bind(&MetricsServicesManager::IsMetricsReportingEnabled,
- base::Unretained(this)));
+ base::Unretained(this)),
+ base::Bind(&GoogleUpdateSettings::StoreMetricsClientInfo),
+ base::Bind(&GoogleUpdateSettings::LoadMetricsClientInfo));
}
return metrics_state_manager_.get();
}
diff --git a/chrome/chrome_installer_util.gypi b/chrome/chrome_installer_util.gypi
index af05c26..ef981c1 100644
--- a/chrome/chrome_installer_util.gypi
+++ b/chrome/chrome_installer_util.gypi
@@ -121,6 +121,7 @@
'<(DEPTH)/chrome/chrome_resources.gyp:chrome_resources',
'<(DEPTH)/chrome/chrome_resources.gyp:chrome_strings',
'<(DEPTH)/chrome/common_constants.gyp:common_constants',
+ '<(DEPTH)/components/components.gyp:metrics',
'<(DEPTH)/courgette/courgette.gyp:courgette_lib',
'<(DEPTH)/crypto/crypto.gyp:crypto',
'<(DEPTH)/third_party/bspatch/bspatch.gyp:bspatch',
@@ -189,6 +190,10 @@
'<(SHARED_INTERMEDIATE_DIR)',
],
'sources': [
+ # Include |client_info.cc| directly here to avoid having to create a
+ # metrics_win64 target solely for this purpose.
+ '../components/metrics/client_info.cc',
+ '../components/metrics/client_info.h',
'installer/util/google_chrome_distribution_dummy.cc',
'installer/util/master_preferences.h',
'installer/util/master_preferences_dummy.cc',
diff --git a/chrome/common/DEPS b/chrome/common/DEPS
index 888c227..7f28bda 100644
--- a/chrome/common/DEPS
+++ b/chrome/common/DEPS
@@ -7,6 +7,7 @@
"+components/bookmarks/common",
"+components/cloud_devices/common",
"+components/data_reduction_proxy/common",
+ "+components/metrics/client_info.h",
"+components/metrics/metrics_pref_names.h",
"+components/nacl/common",
"+components/password_manager/core/common",
diff --git a/chrome/common/child_process_logging_win.cc b/chrome/common/child_process_logging_win.cc
index b849734..37a8d3e 100644
--- a/chrome/common/child_process_logging_win.cc
+++ b/chrome/common/child_process_logging_win.cc
@@ -7,10 +7,12 @@
#include <windows.h>
#include "base/debug/crash_logging.h"
+#include "base/memory/scoped_ptr.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/crash_keys.h"
#include "chrome/installer/util/google_update_settings.h"
+#include "components/metrics/client_info.h"
namespace child_process_logging {
@@ -69,9 +71,10 @@
// because of the aforementioned issue, crash keys aren't ready yet at the
// time of Breakpad initialization, load the client id backed up in Google
// Update settings instead.
- std::string client_guid;
- if (GoogleUpdateSettings::LoadMetricsClientId(&client_guid))
- crash_keys::SetCrashClientIdFromGUID(client_guid);
+ scoped_ptr<metrics::ClientInfo> client_info =
+ GoogleUpdateSettings::LoadMetricsClientInfo();
+ if (client_info)
+ crash_keys::SetCrashClientIdFromGUID(client_info->client_id);
}
} // namespace child_process_logging
diff --git a/chrome/installer/util/DEPS b/chrome/installer/util/DEPS
new file mode 100644
index 0000000..0148070
--- /dev/null
+++ b/chrome/installer/util/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+components/metrics/client_info.h",
+]
diff --git a/chrome/installer/util/google_update_constants.cc b/chrome/installer/util/google_update_constants.cc
index 80c0f1b..431cf7b 100644
--- a/chrome/installer/util/google_update_constants.cc
+++ b/chrome/installer/util/google_update_constants.cc
@@ -45,6 +45,8 @@
const wchar_t kRegLastInstallerExtraField[] = L"LastInstallerExtraCode1";
const wchar_t kRegLastRunTimeField[] = L"lastrun";
const wchar_t kRegMetricsId[] = L"metricsid";
+const wchar_t kRegMetricsIdEnabledDate[] = L"metricsid_enableddate";
+const wchar_t kRegMetricsIdInstallDate[] = L"metricsid_installdate";
const wchar_t kRegMSIField[] = L"msi";
const wchar_t kRegNameField[] = L"name";
const wchar_t kRegOemInstallField[] = L"oeminstall";
diff --git a/chrome/installer/util/google_update_constants.h b/chrome/installer/util/google_update_constants.h
index 9f658ea..8718059 100644
--- a/chrome/installer/util/google_update_constants.h
+++ b/chrome/installer/util/google_update_constants.h
@@ -55,6 +55,8 @@
extern const wchar_t kRegLastInstallerErrorField[];
extern const wchar_t kRegLastInstallerExtraField[];
extern const wchar_t kRegMetricsId[];
+extern const wchar_t kRegMetricsIdEnabledDate[];
+extern const wchar_t kRegMetricsIdInstallDate[];
extern const wchar_t kRegMSIField[];
extern const wchar_t kRegNameField[];
extern const wchar_t kRegOemInstallField[];
diff --git a/chrome/installer/util/google_update_settings.cc b/chrome/installer/util/google_update_settings.cc
index 4d53eca..3c4cba6 100644
--- a/chrome/installer/util/google_update_settings.cc
+++ b/chrome/installer/util/google_update_settings.cc
@@ -296,16 +296,43 @@
return (result == ERROR_SUCCESS);
}
-bool GoogleUpdateSettings::LoadMetricsClientId(std::string* metrics_id) {
- base::string16 metrics_id16;
- bool rv = ReadGoogleUpdateStrKey(google_update::kRegMetricsId, &metrics_id16);
- *metrics_id = base::UTF16ToUTF8(metrics_id16);
- return rv;
+scoped_ptr<metrics::ClientInfo> GoogleUpdateSettings::LoadMetricsClientInfo() {
+ base::string16 client_id_16;
+ if (!ReadGoogleUpdateStrKey(google_update::kRegMetricsId, &client_id_16) ||
+ client_id_16.empty()) {
+ return scoped_ptr<metrics::ClientInfo>();
+ }
+
+ scoped_ptr<metrics::ClientInfo> client_info(new metrics::ClientInfo);
+ client_info->client_id = base::UTF16ToUTF8(client_id_16);
+
+ base::string16 installation_date_str;
+ if (ReadGoogleUpdateStrKey(google_update::kRegMetricsIdInstallDate,
+ &installation_date_str)) {
+ base::StringToInt64(installation_date_str, &client_info->installation_date);
+ }
+
+ base::string16 reporting_enbaled_date_date_str;
+ if (ReadGoogleUpdateStrKey(google_update::kRegMetricsIdEnabledDate,
+ &reporting_enbaled_date_date_str)) {
+ base::StringToInt64(reporting_enbaled_date_date_str,
+ &client_info->reporting_enabled_date);
+ }
+
+ return client_info.Pass();
}
-bool GoogleUpdateSettings::StoreMetricsClientId(const std::string& metrics_id) {
- base::string16 metrics_id16 = base::UTF8ToUTF16(metrics_id);
- return WriteGoogleUpdateStrKey(google_update::kRegMetricsId, metrics_id16);
+void GoogleUpdateSettings::StoreMetricsClientInfo(
+ const metrics::ClientInfo& client_info) {
+ // Attempt a best-effort at backing |client_info| in the registry (but don't
+ // handle/report failures).
+ WriteGoogleUpdateStrKey(google_update::kRegMetricsId,
+ base::UTF8ToUTF16(client_info.client_id));
+ WriteGoogleUpdateStrKey(google_update::kRegMetricsIdInstallDate,
+ base::Int64ToString16(client_info.installation_date));
+ WriteGoogleUpdateStrKey(
+ google_update::kRegMetricsIdEnabledDate,
+ base::Int64ToString16(client_info.reporting_enabled_date));
}
// EULA consent is only relevant for system-level installs.
diff --git a/chrome/installer/util/google_update_settings.h b/chrome/installer/util/google_update_settings.h
index a8e43c4..ca7fd7f 100644
--- a/chrome/installer/util/google_update_settings.h
+++ b/chrome/installer/util/google_update_settings.h
@@ -8,10 +8,12 @@
#include <string>
#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
#include "base/strings/string16.h"
#include "base/time/time.h"
#include "base/version.h"
#include "chrome/installer/util/util_constants.h"
+#include "components/metrics/client_info.h"
class AppRegistrationData;
class BrowserDistribution;
@@ -86,12 +88,15 @@
bool consented);
#endif
- // Returns the metrics client id backed up in the registry. If none found,
- // returns empty string.
- static bool LoadMetricsClientId(std::string* metrics_id);
+ // Returns the metrics client info backed up in the registry. NULL
+ // if-and-only-if the client_id couldn't be retrieved (failure to retrieve
+ // other fields only makes them keep their default value). A non-null return
+ // will NEVER contain an empty client_id field.
+ static scoped_ptr<metrics::ClientInfo> LoadMetricsClientInfo();
- // Stores a backup of the metrics client id in the registry.
- static bool StoreMetricsClientId(const std::string& metrics_id);
+ // Stores a backup of the metrics client info in the registry. Storing a
+ // |client_info| with an empty client id will effectively void the backup.
+ static void StoreMetricsClientInfo(const metrics::ClientInfo& client_info);
// Sets the machine-wide EULA consented flag required on OEM installs.
// Returns false if the setting could not be recorded.
diff --git a/components/metrics.gypi b/components/metrics.gypi
index 964e4fa..cf43cf7 100644
--- a/components/metrics.gypi
+++ b/components/metrics.gypi
@@ -18,10 +18,12 @@
'variations',
],
'sources': [
- 'metrics/compression_utils.cc',
- 'metrics/compression_utils.h',
+ 'metrics/client_info.cc',
+ 'metrics/client_info.h',
'metrics/cloned_install_detector.cc',
'metrics/cloned_install_detector.h',
+ 'metrics/compression_utils.cc',
+ 'metrics/compression_utils.h',
'metrics/machine_id_provider.h',
'metrics/machine_id_provider_stub.cc',
'metrics/machine_id_provider_win.cc',
@@ -29,10 +31,10 @@
'metrics/metrics_hashes.h',
'metrics/metrics_log.cc',
'metrics/metrics_log.h',
- 'metrics/metrics_log_uploader.cc',
- 'metrics/metrics_log_uploader.h',
'metrics/metrics_log_manager.cc',
'metrics/metrics_log_manager.h',
+ 'metrics/metrics_log_uploader.cc',
+ 'metrics/metrics_log_uploader.h',
'metrics/metrics_pref_names.cc',
'metrics/metrics_pref_names.h',
'metrics/metrics_provider.h',
diff --git a/components/metrics/client_info.cc b/components/metrics/client_info.cc
new file mode 100644
index 0000000..57ad394
--- /dev/null
+++ b/components/metrics/client_info.cc
@@ -0,0 +1,13 @@
+// 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 "components/metrics/client_info.h"
+
+namespace metrics {
+
+ClientInfo::ClientInfo() : installation_date(0), reporting_enabled_date(0) {}
+
+ClientInfo::~ClientInfo() {}
+
+} // namespace metrics
diff --git a/components/metrics/client_info.h b/components/metrics/client_info.h
new file mode 100644
index 0000000..8fcaec2
--- /dev/null
+++ b/components/metrics/client_info.h
@@ -0,0 +1,38 @@
+// 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 COMPONENTS_METRICS_CLIENT_INFO_H_
+#define COMPONENTS_METRICS_CLIENT_INFO_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/macros.h"
+
+namespace metrics {
+
+// A data object used to pass data from outside the metrics component into the
+// metrics component.
+struct ClientInfo {
+ public:
+ ClientInfo();
+ ~ClientInfo();
+
+ // The metrics ID of this client: represented as a GUID string.
+ std::string client_id;
+
+ // The installation date: represented as an epoch time in seconds.
+ int64 installation_date;
+
+ // The date on which metrics reporting was enabled: represented as an epoch
+ // time in seconds.
+ int64 reporting_enabled_date;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ClientInfo);
+};
+
+} // namespace metrics
+
+#endif // COMPONENTS_METRICS_CLIENT_INFO_H_
diff --git a/components/metrics/metrics_service_unittest.cc b/components/metrics/metrics_service_unittest.cc
index 694c45a..c580287 100644
--- a/components/metrics/metrics_service_unittest.cc
+++ b/components/metrics/metrics_service_unittest.cc
@@ -7,9 +7,11 @@
#include <string>
#include "base/bind.h"
+#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/prefs/testing_pref_service.h"
#include "base/threading/platform_thread.h"
+#include "components/metrics/client_info.h"
#include "components/metrics/compression_utils.h"
#include "components/metrics/metrics_log.h"
#include "components/metrics/metrics_pref_names.h"
@@ -23,6 +25,13 @@
using metrics::MetricsLogManager;
+void StoreNoClientInfoBackup(const metrics::ClientInfo& /* client_info */) {
+}
+
+scoped_ptr<metrics::ClientInfo> ReturnNoBackup() {
+ return scoped_ptr<metrics::ClientInfo>();
+}
+
class TestMetricsService : public MetricsService {
public:
TestMetricsService(metrics::MetricsStateManager* state_manager,
@@ -62,7 +71,9 @@
metrics_state_manager_ = metrics::MetricsStateManager::Create(
GetLocalState(),
base::Bind(&MetricsServiceTest::is_metrics_reporting_enabled,
- base::Unretained(this)));
+ base::Unretained(this)),
+ base::Bind(&StoreNoClientInfoBackup),
+ base::Bind(&ReturnNoBackup));
}
virtual ~MetricsServiceTest() {
diff --git a/components/metrics/metrics_state_manager.cc b/components/metrics/metrics_state_manager.cc
index 3b71976..08875f3 100644
--- a/components/metrics/metrics_state_manager.cc
+++ b/components/metrics/metrics_state_manager.cc
@@ -45,9 +45,13 @@
MetricsStateManager::MetricsStateManager(
PrefService* local_state,
- const base::Callback<bool(void)>& is_reporting_enabled_callback)
+ const base::Callback<bool(void)>& is_reporting_enabled_callback,
+ const StoreClientInfoCallback& store_client_info,
+ const LoadClientInfoCallback& retrieve_client_info)
: local_state_(local_state),
is_reporting_enabled_callback_(is_reporting_enabled_callback),
+ store_client_info_(store_client_info),
+ load_client_info_(retrieve_client_info),
low_entropy_source_(kLowEntropySourceNotSet),
entropy_source_returned_(ENTROPY_SOURCE_NONE) {
ResetMetricsIDsIfNecessary();
@@ -72,9 +76,50 @@
return;
client_id_ = local_state_->GetString(prefs::kMetricsClientID);
- if (!client_id_.empty())
+ if (!client_id_.empty()) {
+ // It is technically sufficient to only save a backup of the client id when
+ // it is initially generated below, but since the backup was only introduced
+ // in M38, seed it explicitly from here for some time.
+ BackUpCurrentClientInfo();
return;
+ }
+ const scoped_ptr<ClientInfo> client_info_backup =
+ LoadClientInfoAndMaybeMigrate();
+ if (client_info_backup) {
+ client_id_ = client_info_backup->client_id;
+
+ const base::Time now = base::Time::Now();
+
+ // Save the recovered client id and also try to reinstantiate the backup
+ // values for the dates corresponding with that client id in order to avoid
+ // weird scenarios where we could report an old client id with a recent
+ // install date.
+ local_state_->SetString(prefs::kMetricsClientID, client_id_);
+ local_state_->SetInt64(prefs::kInstallDate,
+ client_info_backup->installation_date != 0
+ ? client_info_backup->installation_date
+ : now.ToTimeT());
+ local_state_->SetInt64(prefs::kMetricsReportingEnabledTimestamp,
+ client_info_backup->reporting_enabled_date != 0
+ ? client_info_backup->reporting_enabled_date
+ : now.ToTimeT());
+
+ base::TimeDelta recovered_installation_age;
+ if (client_info_backup->installation_date != 0) {
+ recovered_installation_age =
+ now - base::Time::FromTimeT(client_info_backup->installation_date);
+ }
+ UMA_HISTOGRAM_COUNTS_10000("UMA.ClientIdBackupRecoveredWithAge",
+ recovered_installation_age.InHours());
+
+ // Flush the backup back to persistent storage in case we re-generated
+ // missing data above.
+ BackUpCurrentClientInfo();
+ return;
+ }
+
+ // Failing attempts at getting an existing client ID, generate a new one.
client_id_ = base::GenerateGUID();
local_state_->SetString(prefs::kMetricsClientID, client_id_);
@@ -86,6 +131,8 @@
UMA_HISTOGRAM_BOOLEAN("UMA.ClientIdMigrated", true);
}
local_state_->ClearPref(prefs::kMetricsOldClientID);
+
+ BackUpCurrentClientInfo();
}
void MetricsStateManager::CheckForClonedInstall(
@@ -141,12 +188,16 @@
// static
scoped_ptr<MetricsStateManager> MetricsStateManager::Create(
PrefService* local_state,
- const base::Callback<bool(void)>& is_reporting_enabled_callback) {
+ const base::Callback<bool(void)>& is_reporting_enabled_callback,
+ const StoreClientInfoCallback& store_client_info,
+ const LoadClientInfoCallback& retrieve_client_info) {
scoped_ptr<MetricsStateManager> result;
// Note: |instance_exists_| is updated in the constructor and destructor.
if (!instance_exists_) {
- result.reset(
- new MetricsStateManager(local_state, is_reporting_enabled_callback));
+ result.reset(new MetricsStateManager(local_state,
+ is_reporting_enabled_callback,
+ store_client_info,
+ retrieve_client_info));
}
return result.Pass();
}
@@ -168,6 +219,48 @@
registry->RegisterIntegerPref(prefs::kMetricsOldLowEntropySource, 0);
}
+void MetricsStateManager::BackUpCurrentClientInfo() {
+ ClientInfo client_info;
+ client_info.client_id = client_id_;
+ client_info.installation_date = local_state_->GetInt64(prefs::kInstallDate);
+ client_info.reporting_enabled_date =
+ local_state_->GetInt64(prefs::kMetricsReportingEnabledTimestamp);
+ store_client_info_.Run(client_info);
+}
+
+scoped_ptr<ClientInfo> MetricsStateManager::LoadClientInfoAndMaybeMigrate() {
+ scoped_ptr<metrics::ClientInfo> client_info = load_client_info_.Run();
+
+ // Prior to 2014-07, the client ID was stripped of its dashes before being
+ // saved. Migrate back to a proper GUID if this is the case. This migration
+ // code can be removed in M41+.
+ const size_t kGUIDLengthWithoutDashes = 32U;
+ if (client_info &&
+ client_info->client_id.length() == kGUIDLengthWithoutDashes) {
+ DCHECK(client_info->client_id.find('-') == std::string::npos);
+
+ std::string client_id_with_dashes;
+ client_id_with_dashes.reserve(kGUIDLengthWithoutDashes + 4U);
+ std::string::const_iterator client_id_it = client_info->client_id.begin();
+ for (size_t i = 0; i < kGUIDLengthWithoutDashes + 4U; ++i) {
+ if (i == 8U || i == 13U || i == 18U || i == 23U) {
+ client_id_with_dashes.push_back('-');
+ } else {
+ client_id_with_dashes.push_back(*client_id_it);
+ ++client_id_it;
+ }
+ }
+ DCHECK(client_id_it == client_info->client_id.end());
+ client_info->client_id.assign(client_id_with_dashes);
+ }
+
+ // The GUID retrieved (and possibly fixed above) should be valid unless
+ // retrieval failed.
+ DCHECK(!client_info || base::IsValidGUID(client_info->client_id));
+
+ return client_info.Pass();
+}
+
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
@@ -212,6 +305,9 @@
local_state_->ClearPref(prefs::kMetricsClientID);
local_state_->ClearPref(prefs::kMetricsLowEntropySource);
local_state_->ClearPref(prefs::kMetricsResetIds);
+
+ // Also clear the backed up client info.
+ store_client_info_.Run(ClientInfo());
}
} // namespace metrics
diff --git a/components/metrics/metrics_state_manager.h b/components/metrics/metrics_state_manager.h
index 6f13ee9..30b6285 100644
--- a/components/metrics/metrics_state_manager.h
+++ b/components/metrics/metrics_state_manager.h
@@ -10,8 +10,10 @@
#include "base/basictypes.h"
#include "base/callback.h"
#include "base/gtest_prod_util.h"
+#include "base/macros.h"
#include "base/memory/scoped_ptr.h"
#include "base/metrics/field_trial.h"
+#include "components/metrics/client_info.h"
class PrefService;
class PrefRegistrySimple;
@@ -25,6 +27,15 @@
// not be instantiating or using this class directly.
class MetricsStateManager {
public:
+ // A callback that can be invoked to store client info to persistent storage.
+ // Storing an empty client_id will resulted in the backup being voided.
+ typedef base::Callback<void(const ClientInfo& client_info)>
+ StoreClientInfoCallback;
+
+ // A callback that can be invoked to load client info stored through the
+ // StoreClientInfoCallback.
+ typedef base::Callback<scoped_ptr<ClientInfo>(void)> LoadClientInfoCallback;
+
virtual ~MetricsStateManager();
// Returns true if the user opted in to sending metric reports.
@@ -57,7 +68,9 @@
// of the class exists at a time. Returns NULL if an instance exists already.
static scoped_ptr<MetricsStateManager> Create(
PrefService* local_state,
- const base::Callback<bool(void)>& is_reporting_enabled_callback);
+ const base::Callback<bool(void)>& is_reporting_enabled_callback,
+ const StoreClientInfoCallback& store_client_info,
+ const LoadClientInfoCallback& load_client_info);
// Registers local state prefs used by this class.
static void RegisterPrefs(PrefRegistrySimple* registry);
@@ -81,11 +94,22 @@
// Creates the MetricsStateManager with the given |local_state|. Calls
// |is_reporting_enabled_callback| to query whether metrics reporting is
- // enabled. Clients should instead use Create(), which enforces a single
- // instance of this class is alive at any given time.
+ // enabled. Clients should instead use Create(), which enforces that a single
+ // instance of this class be alive at any given time.
+ // |store_client_info| should back up client info to persistent storage such
+ // that it is later retrievable by |load_client_info|.
MetricsStateManager(
PrefService* local_state,
- const base::Callback<bool(void)>& is_reporting_enabled_callback);
+ const base::Callback<bool(void)>& is_reporting_enabled_callback,
+ const StoreClientInfoCallback& store_client_info,
+ const LoadClientInfoCallback& load_client_info);
+
+ // Backs up the current client info via |store_client_info_|.
+ void BackUpCurrentClientInfo();
+
+ // Loads the client info via |load_client_info_| and potentially migrates it
+ // before returning it if it comes back in its old form.
+ scoped_ptr<ClientInfo> LoadClientInfoAndMaybeMigrate();
// Returns the low entropy source for this client. This is a random value
// that is non-identifying amongst browser clients. This method will
@@ -110,8 +134,18 @@
// Weak pointer to the local state prefs store.
PrefService* const local_state_;
+ // A callback run by this MetricsStateManager to know whether reporting is
+ // enabled.
const base::Callback<bool(void)> is_reporting_enabled_callback_;
+ // A callback run during client id creation so this MetricsStateManager can
+ // store a backup of the newly generated ID.
+ const StoreClientInfoCallback store_client_info_;
+
+ // A callback run if this MetricsStateManager can't get the client id from
+ // its typical location and wants to attempt loading it from this backup.
+ const LoadClientInfoCallback load_client_info_;
+
// The identifier that's sent to the server with the log reports.
std::string client_id_;
diff --git a/components/metrics/metrics_state_manager_unittest.cc b/components/metrics/metrics_state_manager_unittest.cc
index 2f7d85b..0e540a5 100644
--- a/components/metrics/metrics_state_manager_unittest.cc
+++ b/components/metrics/metrics_state_manager_unittest.cc
@@ -10,7 +10,9 @@
#include "base/bind.h"
#include "base/command_line.h"
#include "base/prefs/testing_pref_service.h"
+#include "components/metrics/client_info.h"
#include "components/metrics/metrics_pref_names.h"
+#include "components/metrics/metrics_service.h"
#include "components/metrics/metrics_switches.h"
#include "components/variations/caching_permuted_entropy_provider.h"
#include "components/variations/pref_names.h"
@@ -21,13 +23,17 @@
class MetricsStateManagerTest : public testing::Test {
public:
MetricsStateManagerTest() : is_metrics_reporting_enabled_(false) {
- MetricsStateManager::RegisterPrefs(prefs_.registry());
+ MetricsService::RegisterPrefs(prefs_.registry());
}
scoped_ptr<MetricsStateManager> CreateStateManager() {
return MetricsStateManager::Create(
&prefs_,
base::Bind(&MetricsStateManagerTest::is_metrics_reporting_enabled,
+ base::Unretained(this)),
+ base::Bind(&MetricsStateManagerTest::MockStoreClientInfoBackup,
+ base::Unretained(this)),
+ base::Bind(&MetricsStateManagerTest::LoadFakeClientInfoBackup,
base::Unretained(this))).Pass();
}
@@ -39,11 +45,50 @@
protected:
TestingPrefServiceSimple prefs_;
+ // Last ClientInfo stored by the MetricsStateManager via
+ // MockStoreClientInfoBackup.
+ scoped_ptr<ClientInfo> stored_client_info_backup_;
+
+ // If set, will be returned via LoadFakeClientInfoBackup if requested by the
+ // MetricsStateManager.
+ scoped_ptr<ClientInfo> fake_client_info_backup_;
+
private:
bool is_metrics_reporting_enabled() const {
return is_metrics_reporting_enabled_;
}
+ // Stores the |client_info| in |stored_client_info_backup_| for verification
+ // by the tests later.
+ void MockStoreClientInfoBackup(const ClientInfo& client_info) {
+ stored_client_info_backup_.reset(new ClientInfo);
+ stored_client_info_backup_->client_id = client_info.client_id;
+ stored_client_info_backup_->installation_date =
+ client_info.installation_date;
+ stored_client_info_backup_->reporting_enabled_date =
+ client_info.reporting_enabled_date;
+
+ // Respect the contract that storing an empty client_id voids the existing
+ // backup (required for the last section of the ForceClientIdCreation test
+ // below).
+ if (client_info.client_id.empty())
+ fake_client_info_backup_.reset();
+ }
+
+ // Hands out a copy of |fake_client_info_backup_| if it is set.
+ scoped_ptr<ClientInfo> LoadFakeClientInfoBackup() {
+ if (!fake_client_info_backup_)
+ return scoped_ptr<ClientInfo>();
+
+ scoped_ptr<ClientInfo> backup_copy(new ClientInfo);
+ backup_copy->client_id = fake_client_info_backup_->client_id;
+ backup_copy->installation_date =
+ fake_client_info_backup_->installation_date;
+ backup_copy->reporting_enabled_date =
+ fake_client_info_backup_->reporting_enabled_date;
+ return backup_copy.Pass();
+ }
+
bool is_metrics_reporting_enabled_;
DISALLOW_COPY_AND_ASSIGN(MetricsStateManagerTest);
@@ -171,4 +216,160 @@
EXPECT_NE(kInitialClientId, prefs_.GetString(prefs::kMetricsClientID));
}
+TEST_F(MetricsStateManagerTest, ForceClientIdCreation) {
+ const int64 kFakeInstallationDate = 12345;
+ prefs_.SetInt64(prefs::kInstallDate, kFakeInstallationDate);
+
+ // Holds ClientInfo from previous scoped test for extra checks.
+ scoped_ptr<ClientInfo> previous_client_info;
+
+ {
+ scoped_ptr<MetricsStateManager> state_manager(CreateStateManager());
+
+ // client_id shouldn't be auto-generated if metrics reporting is not
+ // enabled.
+ EXPECT_EQ(std::string(), state_manager->client_id());
+ EXPECT_EQ(0, prefs_.GetInt64(prefs::kMetricsReportingEnabledTimestamp));
+
+ // Confirm that the initial ForceClientIdCreation call creates the client id
+ // and backs it up via MockStoreClientInfoBackup.
+ EXPECT_FALSE(stored_client_info_backup_);
+ state_manager->ForceClientIdCreation();
+ EXPECT_NE(std::string(), state_manager->client_id());
+ EXPECT_GT(prefs_.GetInt64(prefs::kMetricsReportingEnabledTimestamp), 0);
+
+ ASSERT_TRUE(stored_client_info_backup_);
+ EXPECT_EQ(state_manager->client_id(),
+ stored_client_info_backup_->client_id);
+ EXPECT_EQ(kFakeInstallationDate,
+ stored_client_info_backup_->installation_date);
+ EXPECT_EQ(prefs_.GetInt64(prefs::kMetricsReportingEnabledTimestamp),
+ stored_client_info_backup_->reporting_enabled_date);
+
+ previous_client_info = stored_client_info_backup_.Pass();
+ }
+
+ EnableMetricsReporting();
+
+ {
+ EXPECT_FALSE(stored_client_info_backup_);
+
+ scoped_ptr<MetricsStateManager> state_manager(CreateStateManager());
+
+ // client_id should be auto-obtained from the constructor when metrics
+ // reporting is enabled.
+ EXPECT_EQ(previous_client_info->client_id, state_manager->client_id());
+
+ // The backup should also be refreshed when the client id re-initialized.
+ ASSERT_TRUE(stored_client_info_backup_);
+ EXPECT_EQ(previous_client_info->client_id,
+ stored_client_info_backup_->client_id);
+ EXPECT_EQ(kFakeInstallationDate,
+ stored_client_info_backup_->installation_date);
+ EXPECT_EQ(previous_client_info->reporting_enabled_date,
+ stored_client_info_backup_->reporting_enabled_date);
+
+ // Re-forcing client id creation shouldn't cause another backup and
+ // shouldn't affect the existing client id.
+ stored_client_info_backup_.reset();
+ state_manager->ForceClientIdCreation();
+ EXPECT_FALSE(stored_client_info_backup_);
+ EXPECT_EQ(previous_client_info->client_id, state_manager->client_id());
+ }
+
+ const int64 kBackupInstallationDate = 1111;
+ const int64 kBackupReportingEnabledDate = 2222;
+ const char kBackupClientId[] = "AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE";
+ fake_client_info_backup_.reset(new ClientInfo);
+ fake_client_info_backup_->client_id = kBackupClientId;
+ fake_client_info_backup_->installation_date = kBackupInstallationDate;
+ fake_client_info_backup_->reporting_enabled_date =
+ kBackupReportingEnabledDate;
+
+ {
+ // The existence of a backup should result in the same behaviour as
+ // before if we already have a client id.
+
+ EXPECT_FALSE(stored_client_info_backup_);
+
+ scoped_ptr<MetricsStateManager> state_manager(CreateStateManager());
+ EXPECT_EQ(previous_client_info->client_id, state_manager->client_id());
+
+ // The backup should also be refreshed when the client id re-initialized.
+ ASSERT_TRUE(stored_client_info_backup_);
+ EXPECT_EQ(previous_client_info->client_id,
+ stored_client_info_backup_->client_id);
+ EXPECT_EQ(kFakeInstallationDate,
+ stored_client_info_backup_->installation_date);
+ EXPECT_EQ(previous_client_info->reporting_enabled_date,
+ stored_client_info_backup_->reporting_enabled_date);
+ stored_client_info_backup_.reset();
+ }
+
+ prefs_.ClearPref(prefs::kMetricsClientID);
+ prefs_.ClearPref(prefs::kMetricsReportingEnabledTimestamp);
+
+ {
+ // The backup should kick in if the client id has gone missing. It should
+ // replace remaining and missing dates as well.
+
+ EXPECT_FALSE(stored_client_info_backup_);
+
+ scoped_ptr<MetricsStateManager> state_manager(CreateStateManager());
+ EXPECT_EQ(kBackupClientId, state_manager->client_id());
+ EXPECT_EQ(kBackupInstallationDate, prefs_.GetInt64(prefs::kInstallDate));
+ EXPECT_EQ(kBackupReportingEnabledDate,
+ prefs_.GetInt64(prefs::kMetricsReportingEnabledTimestamp));
+
+ EXPECT_TRUE(stored_client_info_backup_);
+ stored_client_info_backup_.reset();
+ }
+
+ const char kNoDashesBackupClientId[] = "AAAAAAAABBBBCCCCDDDDEEEEEEEEEEEE";
+ fake_client_info_backup_.reset(new ClientInfo);
+ fake_client_info_backup_->client_id = kNoDashesBackupClientId;
+
+ prefs_.ClearPref(prefs::kInstallDate);
+ prefs_.ClearPref(prefs::kMetricsClientID);
+ prefs_.ClearPref(prefs::kMetricsReportingEnabledTimestamp);
+
+ {
+ // When running the backup from old-style client ids, dashes should be
+ // re-added. And missing dates in backup should be replaced by Time::Now().
+
+ EXPECT_FALSE(stored_client_info_backup_);
+
+ scoped_ptr<MetricsStateManager> state_manager(CreateStateManager());
+ EXPECT_EQ(kBackupClientId, state_manager->client_id());
+ EXPECT_GT(prefs_.GetInt64(prefs::kInstallDate), 0);
+ EXPECT_GT(prefs_.GetInt64(prefs::kMetricsReportingEnabledTimestamp), 0);
+
+ EXPECT_TRUE(stored_client_info_backup_);
+ previous_client_info = stored_client_info_backup_.Pass();
+ }
+
+ prefs_.SetBoolean(prefs::kMetricsResetIds, true);
+
+ {
+ // Upon request to reset metrics ids, the existing backup should not be
+ // restored.
+
+ EXPECT_FALSE(stored_client_info_backup_);
+
+ scoped_ptr<MetricsStateManager> state_manager(CreateStateManager());
+
+ // A brand new client id should have been generated.
+ EXPECT_NE(std::string(), state_manager->client_id());
+ EXPECT_NE(previous_client_info->client_id, state_manager->client_id());
+
+ // Dates should not have been affected.
+ EXPECT_EQ(previous_client_info->installation_date,
+ prefs_.GetInt64(prefs::kInstallDate));
+ EXPECT_EQ(previous_client_info->reporting_enabled_date,
+ prefs_.GetInt64(prefs::kMetricsReportingEnabledTimestamp));
+
+ stored_client_info_backup_.reset();
+ }
+}
+
} // namespace metrics