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