Index: components/metrics/metrics_state_manager.cc |
diff --git a/components/metrics/metrics_state_manager.cc b/components/metrics/metrics_state_manager.cc |
index 3b71976581b30d789d37c05ea2f0cb1d86bfa658..08875f3b5a617d7fcadd87ca169028547e139620 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 |