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