diff options
author | gab@chromium.org <gab@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-07-22 23:36:53 +0000 |
---|---|---|
committer | gab@chromium.org <gab@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-07-22 23:36:53 +0000 |
commit | 8e885de1c9cd3bec58fbeff156bbc31e15fac053 (patch) | |
tree | 20a9aa7091097de740e3d91bc1cccd2005d55d45 | |
parent | 6a5898ed0fc7a089d26b6adb48aa70bde35916cf (diff) | |
download | chromium_src-8e885de1c9cd3bec58fbeff156bbc31e15fac053.zip chromium_src-8e885de1c9cd3bec58fbeff156bbc31e15fac053.tar.gz chromium_src-8e885de1c9cd3bec58fbeff156bbc31e15fac053.tar.bz2 |
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
22 files changed, 525 insertions, 55 deletions
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 @@ include_rules = [ "+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 @@ bool PreReadExperimentIsActive() { 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 @@ void ChromeContentBrowserClient::AppendExtraCommandLineSwitches( 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 @@ bool GoogleUpdateSettings::SetCollectStatsConsent(bool consented) { } // 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::RegisterPrefs(PrefRegistrySimple* registry) { 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 @@ bool IsMetricsReportingEnabled() { 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, HashExtension) { 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::MetricsStateManager* MetricsServicesManager::GetMetricsStateManager() { 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 @@ include_rules = [ "+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 @@ void Init() { // 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 80c0f1b2..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 kRegLastInstallerErrorField[] = L"LastInstallerError"; 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 kRegLastInstallerResultField[]; 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 @@ bool GoogleUpdateSettings::SetCollectStatsConsentAtLevel(bool system_install, 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 @@ class GoogleUpdateSettings { 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 @@ namespace { 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 @@ class MetricsServiceTest : public testing::Test { 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 @@ bool MetricsStateManager::instance_exists_ = false; 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 @@ void MetricsStateManager::ForceClientIdCreation() { 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 @@ void MetricsStateManager::ForceClientIdCreation() { UMA_HISTOGRAM_BOOLEAN("UMA.ClientIdMigrated", true); } local_state_->ClearPref(prefs::kMetricsOldClientID); + + BackUpCurrentClientInfo(); } void MetricsStateManager::CheckForClonedInstall( @@ -141,12 +188,16 @@ MetricsStateManager::CreateEntropyProvider() { // 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 @@ void MetricsStateManager::RegisterPrefs(PrefRegistrySimple* registry) { 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 @@ void MetricsStateManager::ResetMetricsIDsIfNecessary() { 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 @@ class ClonedInstallDetector; // 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 @@ class MetricsStateManager { // 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 @@ class MetricsStateManager { // 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 @@ class MetricsStateManager { // 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 @@ namespace metrics { 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 @@ class MetricsStateManagerTest : public testing::Test { 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 @@ TEST_F(MetricsStateManagerTest, ResetMetricsIDs) { 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 |