• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef COMPONENTS_METRICS_METRICS_STATE_MANAGER_H_
6 #define COMPONENTS_METRICS_METRICS_STATE_MANAGER_H_
7 
8 #include <memory>
9 #include <string>
10 #include <string_view>
11 
12 #include "base/callback_list.h"
13 #include "base/files/file_path.h"
14 #include "base/functional/callback.h"
15 #include "base/gtest_prod_util.h"
16 #include "base/memory/raw_ptr.h"
17 #include "base/metrics/field_trial.h"
18 #include "build/chromeos_buildflags.h"
19 #include "components/metrics/clean_exit_beacon.h"
20 #include "components/metrics/client_info.h"
21 #include "components/metrics/cloned_install_detector.h"
22 #include "components/metrics/entropy_state.h"
23 #include "components/variations/entropy_provider.h"
24 
25 class PrefService;
26 class PrefRegistrySimple;
27 
28 namespace metrics {
29 
30 class EnabledStateProvider;
31 class MetricsProvider;
32 
33 // Denotes whether this session is a background or foreground session at
34 // startup. May be unknown. A background session refers to the situation in
35 // which the browser process starts; does some work, e.g. servicing a sync; and
36 // ends without ever becoming visible. Note that the point in startup at which
37 // this value is determined is likely before the UI is visible.
38 //
39 // These values are persisted to logs. Entries should not be renumbered and
40 // numeric values should never be reused.
41 enum class StartupVisibility {
42   kUnknown = 0,
43   kBackground = 1,
44   kForeground = 2,
45   kMaxValue = kForeground,
46 };
47 
48 // Denotes the type of EntropyProvider to use for default one-time
49 // randomization.
50 enum class EntropyProviderType {
51   kDefault = 0,  // Enable high entropy randomization if possible.
52   kLow = 1,      // Always use low entropy randomization.
53 };
54 
55 // Options to apply to trial randomization.
56 struct EntropyParams {
57   // The type of entropy to use for default one-time randomization.
58   EntropyProviderType default_entropy_provider_type =
59       EntropyProviderType::kDefault;
60   // Force trial randomization into benchmarking mode, which disables
61   // randomization. Users may also be put in this mode if the
62   // --enable_benchmarking command line flag is passed.
63   bool force_benchmarking_mode = false;
64 };
65 
66 // Responsible for managing MetricsService state prefs, specifically the UMA
67 // client id and low entropy source. Code outside the metrics directory should
68 // not be instantiating or using this class directly.
69 class MetricsStateManager final {
70  public:
71   // A callback that can be invoked to store client info to persistent storage.
72   // Storing an empty client_id will resulted in the backup being voided.
73   typedef base::RepeatingCallback<void(const ClientInfo& client_info)>
74       StoreClientInfoCallback;
75 
76   // A callback that can be invoked to load client info stored through the
77   // StoreClientInfoCallback.
78   typedef base::RepeatingCallback<std::unique_ptr<ClientInfo>(void)>
79       LoadClientInfoCallback;
80 
81   MetricsStateManager(const MetricsStateManager&) = delete;
82   MetricsStateManager& operator=(const MetricsStateManager&) = delete;
83 
84   ~MetricsStateManager();
85 
86   std::unique_ptr<MetricsProvider> GetProvider();
87 
88   // Returns true if the user has consented to sending metric reports, and there
89   // is no other reason to disable reporting. One such reason is client
90   // sampling, and this client isn't in the sample.
91   bool IsMetricsReportingEnabled();
92 
93   // Returns true if Extended Variations Safe Mode is supported on this
94   // platform. Variations Safe Mode is a mechanism that allows Chrome to fall
95   // back to a "safe" seed so that clients can recover from a problematic
96   // experiment, for example, one that causes browser crashes. See the design
97   // doc for more details:
98   // https://docs.google.com/document/d/17UN2pLSa5JZqk8f3LeYZIftXewxqcITotgalTrJvGSY.
99   //
100   // Extended Variations Safe Mode builds on this by allowing clients to recover
101   // from problematic experiments that cause browser crashes earlier on in
102   // startup.
103   bool IsExtendedSafeModeSupported() const;
104 
105   // Returns the client ID for this client, or the empty string if the user is
106   // not opted in to metrics reporting.
client_id()107   const std::string& client_id() const { return client_id_; }
108 
109   // Returns the low entropy sources for this client.
110   int GetLowEntropySource();
111   int GetOldLowEntropySource();
112   int GetPseudoLowEntropySource();
113 
114   // The CleanExitBeacon, used to determine whether the previous Chrome browser
115   // session terminated gracefully.
clean_exit_beacon()116   CleanExitBeacon* clean_exit_beacon() { return &clean_exit_beacon_; }
clean_exit_beacon()117   const CleanExitBeacon* clean_exit_beacon() const {
118     return &clean_exit_beacon_;
119   }
120 
121   // Returns true if the session was deemed a background session during startup.
122   // Note that this is not equivalent to !is_foreground_session() because the
123   // type of session may be unknown.
is_background_session()124   bool is_background_session() const {
125     return startup_visibility_ == StartupVisibility::kBackground;
126   }
127 
128   // Returns true if the session was deemed a foreground session during startup.
129   // Note that this is not equivalent to !is_background_session() because the
130   // type of session may be unknown.
is_foreground_session()131   bool is_foreground_session() const {
132     return startup_visibility_ == StartupVisibility::kForeground;
133   }
134 
135   // Instantiates the FieldTrialList.
136   //
137   // Side effect: Initializes |clean_exit_beacon_|.
138   void InstantiateFieldTrialList();
139 
140   // Signals whether the session has shutdown cleanly. Passing `false` for
141   // |has_session_shutdown_cleanly| means that Chrome has launched and has not
142   // yet shut down safely. Passing `true` signals that Chrome has shut down
143   // safely.
144   //
145   // Seeing a call with `false` without a matching call with `true` suggests
146   // that Chrome crashed or otherwise did not shut down cleanly, e.g. maybe the
147   // OS crashed.
148   //
149   // If |is_extended_safe_mode| is true, then |has_session_shutdown_cleanly| is
150   // written to disk synchronously. If false, a write is scheduled, and for
151   // clients in the Extended Variations Safe Mode experiment, a synchronous
152   // write is done, too.
153   //
154   // Note: |is_extended_safe_mode| should be true only for the Extended
155   // Variations Safe Mode experiment.
156   void LogHasSessionShutdownCleanly(bool has_session_shutdown_cleanly,
157                                     bool is_extended_safe_mode = false);
158 
159   // Forces the client ID to be generated. This is useful in case it's needed
160   // before recording.
161   void ForceClientIdCreation();
162 
163   // Sets the external client id. Useful for callers that want explicit control
164   // of the next metrics client id.
165   void SetExternalClientId(const std::string& id);
166 
167   // Checks if this install was cloned or imaged from another machine. If a
168   // clone is detected, resets the client id and low entropy source. This
169   // should not be called more than once.
170   void CheckForClonedInstall();
171 
172   // Checks if the cloned install detector says that client ids should be reset.
173   bool ShouldResetClientIdsOnClonedInstall();
174 
175   // Wrapper around ClonedInstallDetector::AddOnClonedInstallDetectedCallback().
176   base::CallbackListSubscription AddOnClonedInstallDetectedCallback(
177       base::OnceClosure callback);
178 
179   // Creates entropy providers for trial randomization.
180   //
181   // If this StateManager supports high entropy randomization, and there is
182   // either consent to report metrics or this is the first run of Chrome,
183   // this method returns an entropy provider that has a high source of entropy,
184   // partially based on the client ID or provisional client ID. Otherwise, it
185   // only returns an entropy provider that is based on a low entropy source.
186   //
187   // When |enable_limited_entropy_mode| is true, a limited entropy
188   // randomization source value will be generated for this client. This
189   // parameter can only be false before the limited entropy synthetic trial
190   // completes (See limited_entropy_synthetic_trial.h), after which it should be
191   // removed (TODO(crbug.com/40948861)).
192   std::unique_ptr<const variations::EntropyProviders> CreateEntropyProviders(
193       bool enable_limited_entropy_mode);
194 
cloned_install_detector_for_testing()195   ClonedInstallDetector* cloned_install_detector_for_testing() {
196     return &cloned_install_detector_;
197   }
198 
199   // Creates the MetricsStateManager, enforcing that only a single instance
200   // of the class exists at a time. Returns nullptr if an instance exists
201   // already.
202   //
203   // On Windows, |backup_registry_key| is used to store a backup of the clean
204   // exit beacon. It is ignored on other platforms.
205   //
206   // |user_data_dir| is the path to the client's user data directory. If empty,
207   // a separate file will not be used for Variations Safe Mode prefs.
208   //
209   // |startup_visibility| denotes whether this session is expected to come to
210   // the foreground.
211   static std::unique_ptr<MetricsStateManager> Create(
212       PrefService* local_state,
213       EnabledStateProvider* enabled_state_provider,
214       const std::wstring& backup_registry_key,
215       const base::FilePath& user_data_dir,
216       StartupVisibility startup_visibility = StartupVisibility::kUnknown,
217       EntropyParams entropy_params = {},
218       StoreClientInfoCallback store_client_info = StoreClientInfoCallback(),
219       LoadClientInfoCallback load_client_info = LoadClientInfoCallback(),
220       std::string_view external_client_id = std::string_view());
221 
222   // Registers local state prefs used by this class.
223   static void RegisterPrefs(PrefRegistrySimple* registry);
224 
225  private:
226   FRIEND_TEST_ALL_PREFIXES(MetricsStateManagerTest, CheckProviderResetIds);
227   FRIEND_TEST_ALL_PREFIXES(MetricsStateManagerTest, CheckProviderLogNormal);
228   FRIEND_TEST_ALL_PREFIXES(MetricsStateManagerTest,
229                            CheckProviderLogNormalWithParams);
230   FRIEND_TEST_ALL_PREFIXES(
231       MetricsStateManagerTest,
232       CheckProviderResetIds_PreviousIdOnlyReportInResetSession);
233   FRIEND_TEST_ALL_PREFIXES(MetricsStateManagerTest, EntropySourceUsed_Low);
234   FRIEND_TEST_ALL_PREFIXES(MetricsStateManagerTest, EntropySourceUsed_High);
235   FRIEND_TEST_ALL_PREFIXES(MetricsStateManagerTest,
236                            EntropySourceUsed_High_ExternalClientId);
237   FRIEND_TEST_ALL_PREFIXES(
238       MetricsStateManagerTest,
239       EntropySourceUsed_High_ExternalClientId_MetricsReportingDisabled);
240   FRIEND_TEST_ALL_PREFIXES(MetricsStateManagerTest,
241                            ProvisionalClientId_PromotedToClientId);
242   FRIEND_TEST_ALL_PREFIXES(MetricsStateManagerTest,
243                            ProvisionalClientId_PersistedAcrossFirstRuns);
244   FRIEND_TEST_ALL_PREFIXES(MetricsStateManagerTest, ResetBackup);
245   FRIEND_TEST_ALL_PREFIXES(MetricsStateManagerTest, ResetMetricsIDs);
246 
247   // Designates which entropy source was returned from this class.
248   // This is used for testing to validate that we return the correct source
249   // depending on the state of the service.
250   enum EntropySourceType {
251     ENTROPY_SOURCE_NONE,
252     ENTROPY_SOURCE_LOW,
253     ENTROPY_SOURCE_HIGH,
254     ENTROPY_SOURCE_ENUM_SIZE,
255   };
256 
257   // These values are persisted to logs. Entries should not be renumbered and
258   // numerical values should never be reused.
259   enum class ClientIdSource {
260     // Recorded when the client ID in Local State matches the cached copy.
261     kClientIdMatches = 0,
262     // Recorded when we are somehow missing the cached client ID and we are
263     // able to recover it from the Local State.
264     kClientIdFromLocalState = 1,
265     // Recorded when we are somehow missing the client ID stored in Local State
266     // yet are able to recover it from a backup location.
267     kClientIdBackupRecovered = 2,
268     // Recorded when we are somehow missing the client ID in Local State, cache
269     // and backup and there is no provisional client ID, so a new client ID is
270     // generated.
271     kClientIdNew = 3,
272     // Recorded when we are somehow missing the client ID in Local State, cache
273     // and backup, so we promote the provisional client ID.
274     kClientIdFromProvisionalId = 4,
275     // Recorded when the client ID is passed in from external source.
276     // This is needed for Lacros since the client id is passed in from
277     // ash chrome.
278     kClientIdFromExternal = 5,
279     kMaxValue = kClientIdFromExternal,
280   };
281 
282   // Creates the MetricsStateManager with the given |local_state|. Uses
283   // |enabled_state_provider| to query whether there is consent for metrics
284   // reporting, and if it is enabled. Clients should instead use Create(), which
285   // enforces that a single instance of this class be alive at any given time.
286   // |store_client_info| should back up client info to persistent storage such
287   // that it is later retrievable by |load_client_info|.
288   MetricsStateManager(PrefService* local_state,
289                       EnabledStateProvider* enabled_state_provider,
290                       const std::wstring& backup_registry_key,
291                       const base::FilePath& user_data_dir,
292                       EntropyParams entropy_params,
293                       StartupVisibility startup_visibility,
294                       StoreClientInfoCallback store_client_info,
295                       LoadClientInfoCallback load_client_info,
296                       std::string_view external_client_id);
297 
298   // Returns a MetricsStateManagerProvider instance and sets its
299   // |log_normal_metric_state_.gen| with the provided random seed.
300   std::unique_ptr<MetricsProvider> GetProviderAndSetRandomSeedForTesting(
301       int64_t seed);
302 
303   // Backs up the current client info via |store_client_info_|.
304   void BackUpCurrentClientInfo();
305 
306   // Loads the client info via |load_client_info_|.
307   std::unique_ptr<ClientInfo> LoadClientInfo();
308 
309   // Gets the limited entropy randomization source. For clients that only use
310   // the low entropy source (e.g. Android Webview), this will return the empty
311   // string.
312   std::string_view GetLimitedEntropyRandomizationSource();
313 
314   // Returns the high entropy source for this client, which is composed of a
315   // client ID and the low entropy source. This is intended to be unique for
316   // each install. UMA must be enabled (and |client_id_| must be set) or
317   // |kMetricsProvisionalClientID| must be set before calling this.
318   std::string GetHighEntropySource();
319 
320   // Updates |entropy_source_returned_| with |type| iff the current value is
321   // ENTROPY_SOURCE_NONE and logs the new value in a histogram.
322   void UpdateEntropySourceReturnedValue(EntropySourceType type);
323 
324   // Returns the first entropy source that was returned by this service since
325   // start up, or NONE if neither was returned yet. This is exposed for testing
326   // only.
entropy_source_returned()327   EntropySourceType entropy_source_returned() const {
328     return entropy_source_returned_;
329   }
330 
initial_client_id_for_testing()331   std::string initial_client_id_for_testing() const {
332     return initial_client_id_;
333   }
334 
335   // Reset the client id and low entropy source if the kMetricsResetMetricIDs
336   // pref is true.
337   void ResetMetricsIDsIfNecessary();
338 
339   bool ShouldGenerateProvisionalClientId(bool is_first_run);
340 
341   // Whether an instance of this class exists. Used to enforce that there aren't
342   // multiple instances of this class at a given time.
343   static bool instance_exists_;
344 
345   // Weak pointer to the local state prefs store.
346   const raw_ptr<PrefService> local_state_;
347 
348   // Weak pointer to an enabled state provider. Used to know whether the user
349   // has consented to reporting, and if reporting should be done.
350   raw_ptr<EnabledStateProvider> enabled_state_provider_;
351 
352   // Specified options for controlling trial randomization.
353   const EntropyParams entropy_params_;
354 
355   // A callback run during client id creation so this MetricsStateManager can
356   // store a backup of the newly generated ID.
357   const StoreClientInfoCallback store_client_info_;
358 
359   // A callback run if this MetricsStateManager can't get the client id from
360   // its typical location and wants to attempt loading it from this backup.
361   const LoadClientInfoCallback load_client_info_;
362 
363   // A beacon used to determine whether the previous Chrome browser session
364   // terminated gracefully.
365   CleanExitBeacon clean_exit_beacon_;
366 
367   // The identifier that's sent to the server with the log reports.
368   std::string client_id_;
369 
370   // The client id that was used do field trial randomization. This field should
371   // only be changed when we need to do group assignment. |initial_client_id|
372   // should left blank iff a client id was not used to do field trial
373   // randomization.
374   std::string initial_client_id_;
375 
376   // If not empty, use an external client id passed in from another browser as
377   // |client_id_|. This is needed for the Lacros browser where client id needs
378   // be passed in from ash chrome.
379   std::string external_client_id_;
380 
381   // An instance of EntropyState for getting the entropy source values.
382   EntropyState entropy_state_;
383 
384   // The last entropy source returned by this service, used for testing.
385   EntropySourceType entropy_source_returned_;
386 
387   // The value of prefs::kMetricsResetIds seen upon startup, i.e., the value
388   // that was appropriate in the previous session. Used when reporting previous
389   // session (stability) data.
390   bool metrics_ids_were_reset_;
391 
392   // The value of the metrics id before reseting. Only possibly valid if the
393   // metrics id was reset. May be blank if the metrics id was reset but Chrome
394   // has no record of what the previous metrics id was.
395   std::string previous_client_id_;
396 
397   // The detector for understanding the cloned nature of the install so that we
398   // can reset client ids.
399   ClonedInstallDetector cloned_install_detector_;
400 
401   // The type of session, e.g. a foreground session, at startup. This value is
402   // used only during startup. On Android WebLayer, Android WebView, and iOS,
403   // the visibility is unknown at this point in startup.
404   const StartupVisibility startup_visibility_;
405 
406   // Force enables the creation of a provisional client ID on first run even if
407   // this is not a Chrome-branded build. Used for testing.
408   static bool enable_provisional_client_id_for_testing_;
409 };
410 
411 }  // namespace metrics
412 
413 #endif  // COMPONENTS_METRICS_METRICS_STATE_MANAGER_H_
414