metrics_state_manager.cc 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734
  1. // Copyright 2014 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "components/metrics/metrics_state_manager.h"
  5. #include <cstddef>
  6. #include <cstdint>
  7. #include <limits>
  8. #include <memory>
  9. #include <random>
  10. #include <string>
  11. #include <tuple>
  12. #include <utility>
  13. #include "base/base_switches.h"
  14. #include "base/callback_helpers.h"
  15. #include "base/check.h"
  16. #include "base/command_line.h"
  17. #include "base/debug/leak_annotations.h"
  18. #include "base/guid.h"
  19. #include "base/memory/raw_ptr.h"
  20. #include "base/metrics/histogram_functions.h"
  21. #include "base/metrics/histogram_macros.h"
  22. #include "base/numerics/safe_conversions.h"
  23. #include "base/rand_util.h"
  24. #include "base/strings/string_number_conversions.h"
  25. #include "base/strings/stringprintf.h"
  26. #include "base/threading/thread_restrictions.h"
  27. #include "base/time/time.h"
  28. #include "build/branding_buildflags.h"
  29. #include "build/build_config.h"
  30. #include "components/metrics/cloned_install_detector.h"
  31. #include "components/metrics/enabled_state_provider.h"
  32. #include "components/metrics/entropy_state.h"
  33. #include "components/metrics/metrics_data_validation.h"
  34. #include "components/metrics/metrics_log.h"
  35. #include "components/metrics/metrics_pref_names.h"
  36. #include "components/metrics/metrics_provider.h"
  37. #include "components/metrics/metrics_switches.h"
  38. #include "components/prefs/pref_registry_simple.h"
  39. #include "components/prefs/pref_service.h"
  40. #include "components/variations/entropy_provider.h"
  41. #include "components/variations/field_trial_config/field_trial_util.h"
  42. #include "components/variations/pref_names.h"
  43. #include "components/variations/variations_switches.h"
  44. #include "third_party/metrics_proto/chrome_user_metrics_extension.pb.h"
  45. #include "third_party/metrics_proto/system_profile.pb.h"
  46. namespace metrics {
  47. namespace {
  48. // The argument used to generate a non-identifying entropy source. We want no
  49. // more than 13 bits of entropy, so use this max to return a number in the range
  50. // [0, 7999] as the entropy source (12.97 bits of entropy).
  51. const int kMaxLowEntropySize = 8000;
  52. int64_t ReadEnabledDate(PrefService* local_state) {
  53. return local_state->GetInt64(prefs::kMetricsReportingEnabledTimestamp);
  54. }
  55. int64_t ReadInstallDate(PrefService* local_state) {
  56. return local_state->GetInt64(prefs::kInstallDate);
  57. }
  58. std::string ReadClientId(PrefService* local_state) {
  59. return local_state->GetString(prefs::kMetricsClientID);
  60. }
  61. // Round a timestamp measured in seconds since epoch to one with a granularity
  62. // of an hour. This can be used before uploaded potentially sensitive
  63. // timestamps.
  64. int64_t RoundSecondsToHour(int64_t time_in_seconds) {
  65. return 3600 * (time_in_seconds / 3600);
  66. }
  67. // Records the cloned install histogram.
  68. void LogClonedInstall() {
  69. // Equivalent to UMA_HISTOGRAM_BOOLEAN with the stability flag set.
  70. UMA_STABILITY_HISTOGRAM_ENUMERATION("UMA.IsClonedInstall", 1, 2);
  71. }
  72. // No-op function used to create a MetricsStateManager.
  73. std::unique_ptr<metrics::ClientInfo> NoOpLoadClientInfoBackup() {
  74. return nullptr;
  75. }
  76. // Exits the browser with a helpful error message if an invalid,
  77. // field-trial-related command-line flag was specified.
  78. void ExitWithMessage(const std::string& message) {
  79. puts(message.c_str());
  80. exit(1);
  81. }
  82. // Returns a log normal distribution based on the feature params of
  83. // |kNonUniformityValidationFeature|.
  84. std::lognormal_distribution<double> GetLogNormalDist() {
  85. double mean = kLogNormalMean.Get();
  86. double delta = kLogNormalDelta.Get();
  87. double std_dev = kLogNormalStdDev.Get();
  88. return std::lognormal_distribution<double>(mean + std::log(1.0 + delta),
  89. std_dev);
  90. }
  91. // Used to draw a data point from a log normal distribution.
  92. struct LogNormalMetricState {
  93. LogNormalMetricState()
  94. : dist(GetLogNormalDist()), gen(std::mt19937(base::RandUint64())) {}
  95. // Records the artificial non-uniformity histogram for data validation.
  96. void LogArtificialNonUniformity() {
  97. double rand = dist(gen);
  98. // We pick 10k as the upper bound for this histogram so as to avoid losing
  99. // precision. See comments for |kLogNormalMean|.
  100. base::UmaHistogramCounts10000("UMA.DataValidation.LogNormal",
  101. base::saturated_cast<int>(rand));
  102. }
  103. // A log normal distribution generator generated by the `GetLogNormalDist()`
  104. // function.
  105. std::lognormal_distribution<double> dist;
  106. // The pseudo-random generator used to generate a data point from |dist|.
  107. std::mt19937 gen;
  108. };
  109. class MetricsStateMetricsProvider : public MetricsProvider {
  110. public:
  111. MetricsStateMetricsProvider(
  112. PrefService* local_state,
  113. bool metrics_ids_were_reset,
  114. std::string previous_client_id,
  115. std::string initial_client_id,
  116. ClonedInstallDetector const& cloned_install_detector)
  117. : local_state_(local_state),
  118. metrics_ids_were_reset_(metrics_ids_were_reset),
  119. previous_client_id_(std::move(previous_client_id)),
  120. initial_client_id_(std::move(initial_client_id)),
  121. cloned_install_detector_(cloned_install_detector) {}
  122. MetricsStateMetricsProvider(const MetricsStateMetricsProvider&) = delete;
  123. MetricsStateMetricsProvider& operator=(const MetricsStateMetricsProvider&) =
  124. delete;
  125. // MetricsProvider:
  126. void ProvideSystemProfileMetrics(
  127. SystemProfileProto* system_profile) override {
  128. system_profile->set_uma_enabled_date(
  129. RoundSecondsToHour(ReadEnabledDate(local_state_)));
  130. system_profile->set_install_date(
  131. RoundSecondsToHour(ReadInstallDate(local_state_)));
  132. // Client id in the log shouldn't be different than the |local_state_| one
  133. // except when the client disabled UMA before we populate this field to the
  134. // log. If that's the case, the client id in the |local_state_| should be
  135. // empty and we should set |client_id_was_used_for_trial_assignment| to
  136. // false.
  137. std::string client_id = ReadClientId(local_state_);
  138. system_profile->set_client_id_was_used_for_trial_assignment(
  139. !client_id.empty() && client_id == initial_client_id_);
  140. ClonedInstallInfo cloned =
  141. ClonedInstallDetector::ReadClonedInstallInfo(local_state_);
  142. if (cloned.reset_count == 0)
  143. return;
  144. auto* cloned_install_info = system_profile->mutable_cloned_install_info();
  145. if (metrics_ids_were_reset_) {
  146. // Only report the cloned from client_id in the resetting session.
  147. if (!previous_client_id_.empty()) {
  148. cloned_install_info->set_cloned_from_client_id(
  149. MetricsLog::Hash(previous_client_id_));
  150. }
  151. }
  152. cloned_install_info->set_last_timestamp(
  153. RoundSecondsToHour(cloned.last_reset_timestamp));
  154. cloned_install_info->set_first_timestamp(
  155. RoundSecondsToHour(cloned.first_reset_timestamp));
  156. cloned_install_info->set_count(cloned.reset_count);
  157. }
  158. void ProvidePreviousSessionData(
  159. ChromeUserMetricsExtension* uma_proto) override {
  160. if (metrics_ids_were_reset_) {
  161. LogClonedInstall();
  162. if (!previous_client_id_.empty()) {
  163. // If we know the previous client id, overwrite the client id for the
  164. // previous session log so the log contains the client id at the time
  165. // of the previous session. This allows better attribution of crashes
  166. // to earlier behavior. If the previous client id is unknown, leave
  167. // the current client id.
  168. #if BUILDFLAG(IS_CHROMEOS_ASH)
  169. metrics::structured::NeutrinoDevicesLogWithClientId(
  170. previous_client_id_, metrics::structured::NeutrinoDevicesLocation::
  171. kProvidePreviousSessionData);
  172. #endif // BUILDFLAG(IS_CHROMEOS_ASH)
  173. uma_proto->set_client_id(MetricsLog::Hash(previous_client_id_));
  174. }
  175. }
  176. }
  177. void ProvideCurrentSessionData(
  178. ChromeUserMetricsExtension* uma_proto) override {
  179. if (cloned_install_detector_.ClonedInstallDetectedInCurrentSession())
  180. LogClonedInstall();
  181. log_normal_metric_state_.LogArtificialNonUniformity();
  182. }
  183. // Set a random seed for the random number generator.
  184. void SetRandomSeedForTesting(int64_t seed) {
  185. log_normal_metric_state_.gen = std::mt19937(seed);
  186. }
  187. private:
  188. const raw_ptr<PrefService> local_state_;
  189. const bool metrics_ids_were_reset_;
  190. // |previous_client_id_| is set only (if known) when
  191. // |metrics_ids_were_reset_|
  192. const std::string previous_client_id_;
  193. // The client id that was used to randomize field trials. An empty string if
  194. // the low entropy source was used to do randomization.
  195. const std::string initial_client_id_;
  196. const ClonedInstallDetector& cloned_install_detector_;
  197. LogNormalMetricState log_normal_metric_state_;
  198. };
  199. } // namespace
  200. // static
  201. bool MetricsStateManager::instance_exists_ = false;
  202. // static
  203. bool MetricsStateManager::enable_provisional_client_id_for_testing_ = false;
  204. MetricsStateManager::MetricsStateManager(
  205. PrefService* local_state,
  206. EnabledStateProvider* enabled_state_provider,
  207. const std::wstring& backup_registry_key,
  208. const base::FilePath& user_data_dir,
  209. StartupVisibility startup_visibility,
  210. StoreClientInfoCallback store_client_info,
  211. LoadClientInfoCallback retrieve_client_info,
  212. base::StringPiece external_client_id)
  213. : local_state_(local_state),
  214. enabled_state_provider_(enabled_state_provider),
  215. store_client_info_(std::move(store_client_info)),
  216. load_client_info_(std::move(retrieve_client_info)),
  217. clean_exit_beacon_(backup_registry_key, user_data_dir, local_state),
  218. external_client_id_(external_client_id),
  219. entropy_state_(local_state),
  220. entropy_source_returned_(ENTROPY_SOURCE_NONE),
  221. metrics_ids_were_reset_(false),
  222. startup_visibility_(startup_visibility) {
  223. DCHECK(!store_client_info_.is_null());
  224. DCHECK(!load_client_info_.is_null());
  225. ResetMetricsIDsIfNecessary();
  226. [[maybe_unused]] bool is_first_run = false;
  227. int64_t install_date = local_state_->GetInt64(prefs::kInstallDate);
  228. // Set the install date if this is our first run.
  229. if (install_date == 0) {
  230. local_state_->SetInt64(prefs::kInstallDate, base::Time::Now().ToTimeT());
  231. is_first_run = true;
  232. }
  233. if (enabled_state_provider_->IsConsentGiven()) {
  234. #if BUILDFLAG(IS_CHROMEOS_ASH)
  235. metrics::structured::NeutrinoDevicesLogWithClientId(
  236. client_id_,
  237. metrics::structured::NeutrinoDevicesLocation::kMetricsStateManager);
  238. #endif // BUILDFLAG(IS_CHROMEOS_ASH)
  239. ForceClientIdCreation();
  240. } else {
  241. #if BUILDFLAG(IS_ANDROID)
  242. // If on start up we determine that the client has not given their consent
  243. // to report their metrics, the new sampling trial should be used to
  244. // determine whether the client is sampled in or out (if the user ever
  245. // enables metrics reporting). This covers users that are going through
  246. // the first run, as well as users that have metrics reporting disabled.
  247. //
  248. // See crbug/1306481 and the comment above |kUsePostFREFixSamplingTrial| in
  249. // components/metrics/metrics_pref_names.cc for more details.
  250. local_state_->SetBoolean(metrics::prefs::kUsePostFREFixSamplingTrial, true);
  251. #endif // BUILDFLAG(IS_ANDROID)
  252. }
  253. // Generate and store a provisional client ID if necessary. This ID will be
  254. // used for field trial randomization on first run (and possibly in future
  255. // runs if the user closes Chrome during the FRE) and will be promoted to
  256. // become the client ID if UMA is enabled during this session, via the logic
  257. // in ForceClientIdCreation(). If UMA is disabled (refused), we discard it.
  258. //
  259. // Note: This means that if a provisional client ID is used for this session,
  260. // and the user disables (refuses) UMA, then starting from the next run, the
  261. // field trial randomization (group assignment) will be different.
  262. if (ShouldGenerateProvisionalClientId(is_first_run)) {
  263. local_state_->SetString(prefs::kMetricsProvisionalClientID,
  264. base::GenerateGUID());
  265. }
  266. // The |initial_client_id_| should only be set if UMA is enabled or there's a
  267. // provisional client id.
  268. initial_client_id_ =
  269. (client_id_.empty()
  270. ? local_state_->GetString(prefs::kMetricsProvisionalClientID)
  271. : client_id_);
  272. DCHECK(!instance_exists_);
  273. instance_exists_ = true;
  274. }
  275. MetricsStateManager::~MetricsStateManager() {
  276. DCHECK(instance_exists_);
  277. instance_exists_ = false;
  278. }
  279. std::unique_ptr<MetricsProvider> MetricsStateManager::GetProvider() {
  280. return std::make_unique<MetricsStateMetricsProvider>(
  281. local_state_, metrics_ids_were_reset_, previous_client_id_,
  282. initial_client_id_, cloned_install_detector_);
  283. }
  284. std::unique_ptr<MetricsProvider>
  285. MetricsStateManager::GetProviderAndSetRandomSeedForTesting(int64_t seed) {
  286. auto provider = std::make_unique<MetricsStateMetricsProvider>(
  287. local_state_, metrics_ids_were_reset_, previous_client_id_,
  288. initial_client_id_, cloned_install_detector_);
  289. provider->SetRandomSeedForTesting(seed); // IN-TEST
  290. return provider;
  291. }
  292. bool MetricsStateManager::IsMetricsReportingEnabled() {
  293. return enabled_state_provider_->IsReportingEnabled();
  294. }
  295. bool MetricsStateManager::IsExtendedSafeModeSupported() const {
  296. return clean_exit_beacon_.IsExtendedSafeModeSupported();
  297. }
  298. int MetricsStateManager::GetLowEntropySource() {
  299. return entropy_state_.GetLowEntropySource();
  300. }
  301. void MetricsStateManager::InstantiateFieldTrialList(
  302. const char* enable_gpu_benchmarking_switch,
  303. EntropyProviderType entropy_provider_type) {
  304. // Instantiate the FieldTrialList to support field trials. If an instance
  305. // already exists, this is likely a test scenario with a ScopedFeatureList, so
  306. // use the existing instance so that any overrides are still applied.
  307. if (!base::FieldTrialList::GetInstance()) {
  308. std::unique_ptr<const base::FieldTrial::EntropyProvider> entropy_provider =
  309. entropy_provider_type == EntropyProviderType::kLow
  310. ? CreateLowEntropyProvider()
  311. : CreateDefaultEntropyProvider();
  312. // This is intentionally leaked since it needs to live for the duration of
  313. // the browser process and there's no benefit in cleaning it up at exit.
  314. base::FieldTrialList* leaked_field_trial_list =
  315. new base::FieldTrialList(std::move(entropy_provider));
  316. ANNOTATE_LEAKING_OBJECT_PTR(leaked_field_trial_list);
  317. std::ignore = leaked_field_trial_list;
  318. }
  319. // TODO(crbug/1257204): Some FieldTrial-setup-related code is here and some is
  320. // in VariationsFieldTrialCreator::SetUpFieldTrials(). It's not ideal that
  321. // it's in two places.
  322. //
  323. // When benchmarking is enabled, field trials' default groups are chosen, so
  324. // see whether benchmarking needs to be enabled here, before any field trials
  325. // are created.
  326. const base::CommandLine* command_line =
  327. base::CommandLine::ForCurrentProcess();
  328. // TODO(crbug/1251680): See whether it's possible to consolidate the switches.
  329. if (command_line->HasSwitch(variations::switches::kEnableBenchmarking) ||
  330. (enable_gpu_benchmarking_switch &&
  331. command_line->HasSwitch(enable_gpu_benchmarking_switch))) {
  332. base::FieldTrial::EnableBenchmarking();
  333. }
  334. if (command_line->HasSwitch(variations::switches::kForceFieldTrialParams)) {
  335. bool result =
  336. variations::AssociateParamsFromString(command_line->GetSwitchValueASCII(
  337. variations::switches::kForceFieldTrialParams));
  338. if (!result) {
  339. // Some field trial params implement things like csv or json with a
  340. // particular param. If some control characters are not %-encoded, it can
  341. // lead to confusing error messages, so add a hint here.
  342. ExitWithMessage(base::StringPrintf(
  343. "Invalid --%s list specified. Make sure you %%-"
  344. "encode the following characters in param values: %%:/.,",
  345. variations::switches::kForceFieldTrialParams));
  346. }
  347. }
  348. // Ensure any field trials specified on the command line are initialized.
  349. if (command_line->HasSwitch(::switches::kForceFieldTrials)) {
  350. // Create field trials without activating them, so that this behaves in a
  351. // consistent manner with field trials created from the server.
  352. bool result = base::FieldTrialList::CreateTrialsFromString(
  353. command_line->GetSwitchValueASCII(::switches::kForceFieldTrials));
  354. if (!result) {
  355. ExitWithMessage(base::StringPrintf("Invalid --%s list specified.",
  356. ::switches::kForceFieldTrials));
  357. }
  358. }
  359. // Initializing the CleanExitBeacon is done after FieldTrialList instantiation
  360. // to allow experimentation on the CleanExitBeacon.
  361. clean_exit_beacon_.Initialize();
  362. }
  363. void MetricsStateManager::LogHasSessionShutdownCleanly(
  364. bool has_session_shutdown_cleanly,
  365. bool is_extended_safe_mode) {
  366. clean_exit_beacon_.WriteBeaconValue(has_session_shutdown_cleanly,
  367. is_extended_safe_mode);
  368. }
  369. void MetricsStateManager::ForceClientIdCreation() {
  370. // TODO(asvitkine): Ideally, all tests would actually set up consent properly,
  371. // so the command-line checks wouldn't be needed here.
  372. // Currently, kForceEnableMetricsReporting is used by Java UkmTest and
  373. // kMetricsRecordingOnly is used by Chromedriver tests.
  374. DCHECK(enabled_state_provider_->IsConsentGiven() ||
  375. IsMetricsReportingForceEnabled() || IsMetricsRecordingOnlyEnabled());
  376. if (!external_client_id_.empty()) {
  377. client_id_ = external_client_id_;
  378. base::UmaHistogramEnumeration("UMA.ClientIdSource",
  379. ClientIdSource::kClientIdFromExternal);
  380. local_state_->SetString(prefs::kMetricsClientID, client_id_);
  381. return;
  382. }
  383. #if BUILDFLAG(IS_CHROMEOS_ASH)
  384. std::string previous_client_id = client_id_;
  385. #endif // BUILDFLAG(IS_CHROMEOS_ASH)
  386. {
  387. std::string client_id_from_prefs = ReadClientId(local_state_);
  388. // If client id in prefs matches the cached copy, return early.
  389. if (!client_id_from_prefs.empty() && client_id_from_prefs == client_id_) {
  390. base::UmaHistogramEnumeration("UMA.ClientIdSource",
  391. ClientIdSource::kClientIdMatches);
  392. return;
  393. }
  394. client_id_.swap(client_id_from_prefs);
  395. }
  396. if (!client_id_.empty()) {
  397. base::UmaHistogramEnumeration("UMA.ClientIdSource",
  398. ClientIdSource::kClientIdFromLocalState);
  399. #if BUILDFLAG(IS_CHROMEOS_ASH)
  400. LogClientIdChanged(
  401. metrics::structured::NeutrinoDevicesLocation::kClientIdFromLocalState,
  402. previous_client_id);
  403. #endif // BUILDFLAG(IS_CHROMEOS_ASH)
  404. return;
  405. }
  406. const std::unique_ptr<ClientInfo> client_info_backup = LoadClientInfo();
  407. if (client_info_backup) {
  408. client_id_ = client_info_backup->client_id;
  409. const base::Time now = base::Time::Now();
  410. // Save the recovered client id and also try to reinstantiate the backup
  411. // values for the dates corresponding with that client id in order to avoid
  412. // weird scenarios where we could report an old client id with a recent
  413. // install date.
  414. local_state_->SetString(prefs::kMetricsClientID, client_id_);
  415. local_state_->SetInt64(prefs::kInstallDate,
  416. client_info_backup->installation_date != 0
  417. ? client_info_backup->installation_date
  418. : now.ToTimeT());
  419. local_state_->SetInt64(prefs::kMetricsReportingEnabledTimestamp,
  420. client_info_backup->reporting_enabled_date != 0
  421. ? client_info_backup->reporting_enabled_date
  422. : now.ToTimeT());
  423. base::TimeDelta recovered_installation_age;
  424. if (client_info_backup->installation_date != 0) {
  425. recovered_installation_age =
  426. now - base::Time::FromTimeT(client_info_backup->installation_date);
  427. }
  428. base::UmaHistogramEnumeration("UMA.ClientIdSource",
  429. ClientIdSource::kClientIdBackupRecovered);
  430. base::UmaHistogramCounts10000("UMA.ClientIdBackupRecoveredWithAge",
  431. recovered_installation_age.InHours());
  432. #if BUILDFLAG(IS_CHROMEOS_ASH)
  433. LogClientIdChanged(
  434. metrics::structured::NeutrinoDevicesLocation::kClientIdBackupRecovered,
  435. previous_client_id);
  436. #endif // BUILDFLAG(IS_CHROMEOS_ASH)
  437. // Flush the backup back to persistent storage in case we re-generated
  438. // missing data above.
  439. BackUpCurrentClientInfo();
  440. return;
  441. }
  442. // If we're here, there was no client ID yet (either in prefs or backup),
  443. // so generate a new one. If there's a provisional client id (e.g. UMA
  444. // was enabled as part of first run), promote that to the client id,
  445. // otherwise (e.g. UMA enabled in a future session), generate a new one.
  446. std::string provisional_client_id =
  447. local_state_->GetString(prefs::kMetricsProvisionalClientID);
  448. if (provisional_client_id.empty()) {
  449. client_id_ = base::GenerateGUID();
  450. base::UmaHistogramEnumeration("UMA.ClientIdSource",
  451. ClientIdSource::kClientIdNew);
  452. #if BUILDFLAG(IS_CHROMEOS_ASH)
  453. LogClientIdChanged(
  454. metrics::structured::NeutrinoDevicesLocation::kClientIdNew,
  455. previous_client_id);
  456. #endif // BUILDFLAG(IS_CHROMEOS_ASH)
  457. } else {
  458. client_id_ = provisional_client_id;
  459. local_state_->ClearPref(prefs::kMetricsProvisionalClientID);
  460. base::UmaHistogramEnumeration("UMA.ClientIdSource",
  461. ClientIdSource::kClientIdFromProvisionalId);
  462. #if BUILDFLAG(IS_CHROMEOS_ASH)
  463. LogClientIdChanged(metrics::structured::NeutrinoDevicesLocation::
  464. kClientIdFromProvisionalId,
  465. previous_client_id);
  466. #endif // BUILDFLAG(IS_CHROMEOS_ASH)
  467. }
  468. local_state_->SetString(prefs::kMetricsClientID, client_id_);
  469. // Record the timestamp of when the user opted in to UMA.
  470. local_state_->SetInt64(prefs::kMetricsReportingEnabledTimestamp,
  471. base::Time::Now().ToTimeT());
  472. BackUpCurrentClientInfo();
  473. }
  474. void MetricsStateManager::SetExternalClientId(const std::string& id) {
  475. external_client_id_ = id;
  476. }
  477. void MetricsStateManager::CheckForClonedInstall() {
  478. cloned_install_detector_.CheckForClonedInstall(local_state_);
  479. }
  480. bool MetricsStateManager::ShouldResetClientIdsOnClonedInstall() {
  481. return cloned_install_detector_.ShouldResetClientIds(local_state_);
  482. }
  483. std::unique_ptr<const base::FieldTrial::EntropyProvider>
  484. MetricsStateManager::CreateDefaultEntropyProvider() {
  485. // |initial_client_id_| should be populated iff (a) we have the client's
  486. // consent to enable UMA on startup or (b) it's the first run, in which case
  487. // |initial_client_id_| corresponds to |provisional_client_id_|.
  488. if (!initial_client_id_.empty()) {
  489. UpdateEntropySourceReturnedValue(ENTROPY_SOURCE_HIGH);
  490. return std::make_unique<variations::SHA1EntropyProvider>(
  491. GetHighEntropySource());
  492. }
  493. UpdateEntropySourceReturnedValue(ENTROPY_SOURCE_LOW);
  494. return CreateLowEntropyProvider();
  495. }
  496. std::unique_ptr<const base::FieldTrial::EntropyProvider>
  497. MetricsStateManager::CreateLowEntropyProvider() {
  498. int source = GetLowEntropySource();
  499. return std::make_unique<variations::NormalizedMurmurHashEntropyProvider>(
  500. base::checked_cast<uint16_t>(source), kMaxLowEntropySize);
  501. }
  502. // static
  503. std::unique_ptr<MetricsStateManager> MetricsStateManager::Create(
  504. PrefService* local_state,
  505. EnabledStateProvider* enabled_state_provider,
  506. const std::wstring& backup_registry_key,
  507. const base::FilePath& user_data_dir,
  508. StartupVisibility startup_visibility,
  509. StoreClientInfoCallback store_client_info,
  510. LoadClientInfoCallback retrieve_client_info,
  511. base::StringPiece external_client_id) {
  512. std::unique_ptr<MetricsStateManager> result;
  513. // Note: |instance_exists_| is updated in the constructor and destructor.
  514. if (!instance_exists_) {
  515. result.reset(new MetricsStateManager(
  516. local_state, enabled_state_provider, backup_registry_key, user_data_dir,
  517. startup_visibility,
  518. store_client_info.is_null() ? base::DoNothing()
  519. : std::move(store_client_info),
  520. retrieve_client_info.is_null()
  521. ? base::BindRepeating(&NoOpLoadClientInfoBackup)
  522. : std::move(retrieve_client_info),
  523. external_client_id));
  524. }
  525. return result;
  526. }
  527. // static
  528. void MetricsStateManager::RegisterPrefs(PrefRegistrySimple* registry) {
  529. registry->RegisterStringPref(prefs::kMetricsProvisionalClientID,
  530. std::string());
  531. registry->RegisterStringPref(prefs::kMetricsClientID, std::string());
  532. registry->RegisterInt64Pref(prefs::kMetricsReportingEnabledTimestamp, 0);
  533. registry->RegisterInt64Pref(prefs::kInstallDate, 0);
  534. #if BUILDFLAG(IS_ANDROID)
  535. registry->RegisterBooleanPref(prefs::kUsePostFREFixSamplingTrial, false);
  536. #endif // BUILDFLAG(IS_ANDROID)
  537. EntropyState::RegisterPrefs(registry);
  538. ClonedInstallDetector::RegisterPrefs(registry);
  539. }
  540. void MetricsStateManager::BackUpCurrentClientInfo() {
  541. ClientInfo client_info;
  542. client_info.client_id = client_id_;
  543. client_info.installation_date = ReadInstallDate(local_state_);
  544. client_info.reporting_enabled_date = ReadEnabledDate(local_state_);
  545. store_client_info_.Run(client_info);
  546. }
  547. std::unique_ptr<ClientInfo> MetricsStateManager::LoadClientInfo() {
  548. // If a cloned install was detected, loading ClientInfo from backup will be
  549. // a race condition with clearing the backup. Skip all backup reads for this
  550. // session.
  551. if (metrics_ids_were_reset_)
  552. return nullptr;
  553. std::unique_ptr<ClientInfo> client_info = load_client_info_.Run();
  554. // The GUID retrieved should be valid unless retrieval failed.
  555. // If not, return nullptr. This will result in a new GUID being generated by
  556. // the calling function ForceClientIdCreation().
  557. if (client_info && !base::IsValidGUID(client_info->client_id))
  558. return nullptr;
  559. return client_info;
  560. }
  561. std::string MetricsStateManager::GetHighEntropySource() {
  562. // This should only be called if the |initial_client_id_| is not empty. The
  563. // user shouldn't be able to enable UMA between the constructor and calling
  564. // this, because field trial setup happens at Chrome initialization.
  565. DCHECK(!initial_client_id_.empty());
  566. return entropy_state_.GetHighEntropySource(initial_client_id_);
  567. }
  568. int MetricsStateManager::GetOldLowEntropySource() {
  569. return entropy_state_.GetOldLowEntropySource();
  570. }
  571. void MetricsStateManager::UpdateEntropySourceReturnedValue(
  572. EntropySourceType type) {
  573. if (entropy_source_returned_ != ENTROPY_SOURCE_NONE)
  574. return;
  575. entropy_source_returned_ = type;
  576. base::UmaHistogramEnumeration("UMA.EntropySourceType", type,
  577. ENTROPY_SOURCE_ENUM_SIZE);
  578. }
  579. void MetricsStateManager::ResetMetricsIDsIfNecessary() {
  580. if (!ShouldResetClientIdsOnClonedInstall())
  581. return;
  582. metrics_ids_were_reset_ = true;
  583. previous_client_id_ = ReadClientId(local_state_);
  584. base::UmaHistogramBoolean("UMA.MetricsIDsReset", true);
  585. DCHECK(client_id_.empty());
  586. local_state_->ClearPref(prefs::kMetricsClientID);
  587. EntropyState::ClearPrefs(local_state_);
  588. ClonedInstallDetector::RecordClonedInstallInfo(local_state_);
  589. // Also clear the backed up client info. This is asynchronus; any reads
  590. // shortly after may retrieve the old ClientInfo from the backup.
  591. store_client_info_.Run(ClientInfo());
  592. }
  593. bool MetricsStateManager::ShouldGenerateProvisionalClientId(bool is_first_run) {
  594. #if BUILDFLAG(IS_WIN)
  595. // We do not want to generate a provisional client ID on Windows because
  596. // there's no UMA checkbox on first run. Instead it comes from the install
  597. // page. So if UMA is not enabled at this point, it's unlikely it will be
  598. // enabled in the same session since that requires the user to manually do
  599. // that via settings page after they unchecked it on the download page.
  600. //
  601. // Note: Windows first run is covered by browser tests
  602. // FirstRunMasterPrefsVariationsSeedTest.PRE_SecondRun and
  603. // FirstRunMasterPrefsVariationsSeedTest.SecondRun. If the platform ifdef
  604. // for this logic changes, the tests should be updated as well.
  605. return false;
  606. #else
  607. // We should only generate a provisional client ID on the first run. If for
  608. // some reason there is already a client ID, we do not generate one either.
  609. // This can happen if metrics reporting is managed by a policy.
  610. if (!is_first_run || !client_id_.empty())
  611. return false;
  612. // Return false if |kMetricsReportingEnabled| is managed by a policy. For
  613. // example, if metrics reporting is disabled by a policy, then
  614. // |kMetricsReportingEnabled| will always be set to false, so there is no
  615. // reason to generate a provisional client ID. If metrics reporting is enabled
  616. // by a policy, then the default value of |kMetricsReportingEnabled| will be
  617. // true, and so a client ID will have already been generated (we would have
  618. // returned false already because of the previous check).
  619. if (local_state_->IsManagedPreference(prefs::kMetricsReportingEnabled))
  620. return false;
  621. // If this is a non-Google-Chrome-branded build, we do not want to generate a
  622. // provisional client ID because metrics reporting is not enabled on those
  623. // builds. This would be problematic because we store the provisional client
  624. // ID in the Local State, and clear it when either 1) we enable UMA (the
  625. // provisional client ID becomes the client ID), or 2) we disable UMA. Since
  626. // in non-Google-Chrome-branded builds we never actually go through the code
  627. // paths to either enable or disable UMA, the pref storing the provisional
  628. // client ID would never be cleared. However, for test consistency between
  629. // the different builds, we do not return false here if
  630. // |enable_provisional_client_id_for_testing_| is set to true.
  631. if (!BUILDFLAG(GOOGLE_CHROME_BRANDING) &&
  632. !enable_provisional_client_id_for_testing_) {
  633. return false;
  634. }
  635. return true;
  636. #endif // BUILDFLAG(IS_WIN)
  637. }
  638. #if BUILDFLAG(IS_CHROMEOS_ASH)
  639. void MetricsStateManager::LogClientIdChanged(
  640. metrics::structured::NeutrinoDevicesLocation location,
  641. std::string previous_client_id) {
  642. metrics::structured::NeutrinoDevicesLogClientIdChanged(
  643. client_id_, previous_client_id, ReadInstallDate(local_state_),
  644. ReadEnabledDate(local_state_), location);
  645. }
  646. #endif // BUILDFLAG(IS_CHROMEOS_ASH)
  647. } // namespace metrics