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 // The CleanExitBeacon, used to determine whether the previous Chrome browser 114 // session terminated gracefully. clean_exit_beacon()115 CleanExitBeacon* clean_exit_beacon() { return &clean_exit_beacon_; } clean_exit_beacon()116 const CleanExitBeacon* clean_exit_beacon() const { 117 return &clean_exit_beacon_; 118 } 119 120 // Returns true if the session was deemed a background session during startup. 121 // Note that this is not equivalent to !is_foreground_session() because the 122 // type of session may be unknown. is_background_session()123 bool is_background_session() const { 124 return startup_visibility_ == StartupVisibility::kBackground; 125 } 126 127 // Returns true if the session was deemed a foreground session during startup. 128 // Note that this is not equivalent to !is_background_session() because the 129 // type of session may be unknown. is_foreground_session()130 bool is_foreground_session() const { 131 return startup_visibility_ == StartupVisibility::kForeground; 132 } 133 134 // Instantiates the FieldTrialList. 135 // 136 // Side effect: Initializes |clean_exit_beacon_|. 137 void InstantiateFieldTrialList(); 138 139 // Signals whether the session has shutdown cleanly. Passing `false` for 140 // |has_session_shutdown_cleanly| means that Chrome has launched and has not 141 // yet shut down safely. Passing `true` signals that Chrome has shut down 142 // safely. 143 // 144 // Seeing a call with `false` without a matching call with `true` suggests 145 // that Chrome crashed or otherwise did not shut down cleanly, e.g. maybe the 146 // OS crashed. 147 // 148 // If |is_extended_safe_mode| is true, then |has_session_shutdown_cleanly| is 149 // written to disk synchronously. If false, a write is scheduled, and for 150 // clients in the Extended Variations Safe Mode experiment, a synchronous 151 // write is done, too. 152 // 153 // Note: |is_extended_safe_mode| should be true only for the Extended 154 // Variations Safe Mode experiment. 155 void LogHasSessionShutdownCleanly(bool has_session_shutdown_cleanly, 156 bool is_extended_safe_mode = false); 157 158 // Forces the client ID to be generated. This is useful in case it's needed 159 // before recording. 160 void ForceClientIdCreation(); 161 162 // Sets the external client id. Useful for callers that want explicit control 163 // of the next metrics client id. 164 void SetExternalClientId(const std::string& id); 165 166 // Checks if this install was cloned or imaged from another machine. If a 167 // clone is detected, resets the client id and low entropy source. This 168 // should not be called more than once. 169 void CheckForClonedInstall(); 170 171 // Checks if the cloned install detector says that client ids should be reset. 172 bool ShouldResetClientIdsOnClonedInstall(); 173 174 // Wrapper around ClonedInstallDetector::AddOnClonedInstallDetectedCallback(). 175 base::CallbackListSubscription AddOnClonedInstallDetectedCallback( 176 base::OnceClosure callback); 177 178 // Creates entropy providers for trial randomization. 179 // 180 // If this StateManager supports high entropy randomization, and there is 181 // either consent to report metrics or this is the first run of Chrome, 182 // this method returns an entropy provider that has a high source of entropy, 183 // partially based on the client ID or provisional client ID. Otherwise, it 184 // only returns an entropy provider that is based on a low entropy source. 185 std::unique_ptr<const variations::EntropyProviders> CreateEntropyProviders(); 186 cloned_install_detector_for_testing()187 ClonedInstallDetector* cloned_install_detector_for_testing() { 188 return &cloned_install_detector_; 189 } 190 191 // Creates the MetricsStateManager, enforcing that only a single instance 192 // of the class exists at a time. Returns nullptr if an instance exists 193 // already. 194 // 195 // On Windows, |backup_registry_key| is used to store a backup of the clean 196 // exit beacon. It is ignored on other platforms. 197 // 198 // |user_data_dir| is the path to the client's user data directory. If empty, 199 // a separate file will not be used for Variations Safe Mode prefs. 200 // 201 // |startup_visibility| denotes whether this session is expected to come to 202 // the foreground. 203 static std::unique_ptr<MetricsStateManager> Create( 204 PrefService* local_state, 205 EnabledStateProvider* enabled_state_provider, 206 const std::wstring& backup_registry_key, 207 const base::FilePath& user_data_dir, 208 StartupVisibility startup_visibility = StartupVisibility::kUnknown, 209 EntropyParams entropy_params = {}, 210 StoreClientInfoCallback store_client_info = StoreClientInfoCallback(), 211 LoadClientInfoCallback load_client_info = LoadClientInfoCallback(), 212 base::StringPiece external_client_id = base::StringPiece()); 213 214 // Registers local state prefs used by this class. 215 static void RegisterPrefs(PrefRegistrySimple* registry); 216 217 private: 218 FRIEND_TEST_ALL_PREFIXES(MetricsStateManagerTest, CheckProviderResetIds); 219 FRIEND_TEST_ALL_PREFIXES(MetricsStateManagerTest, CheckProviderLogNormal); 220 FRIEND_TEST_ALL_PREFIXES(MetricsStateManagerTest, 221 CheckProviderLogNormalWithParams); 222 FRIEND_TEST_ALL_PREFIXES( 223 MetricsStateManagerTest, 224 CheckProviderResetIds_PreviousIdOnlyReportInResetSession); 225 FRIEND_TEST_ALL_PREFIXES(MetricsStateManagerTest, EntropySourceUsed_Low); 226 FRIEND_TEST_ALL_PREFIXES(MetricsStateManagerTest, EntropySourceUsed_High); 227 FRIEND_TEST_ALL_PREFIXES(MetricsStateManagerTest, 228 ProvisionalClientId_PromotedToClientId); 229 FRIEND_TEST_ALL_PREFIXES(MetricsStateManagerTest, 230 ProvisionalClientId_PersistedAcrossFirstRuns); 231 FRIEND_TEST_ALL_PREFIXES(MetricsStateManagerTest, ResetBackup); 232 FRIEND_TEST_ALL_PREFIXES(MetricsStateManagerTest, ResetMetricsIDs); 233 234 // Designates which entropy source was returned from this class. 235 // This is used for testing to validate that we return the correct source 236 // depending on the state of the service. 237 enum EntropySourceType { 238 ENTROPY_SOURCE_NONE, 239 ENTROPY_SOURCE_LOW, 240 ENTROPY_SOURCE_HIGH, 241 ENTROPY_SOURCE_ENUM_SIZE, 242 }; 243 244 // These values are persisted to logs. Entries should not be renumbered and 245 // numerical values should never be reused. 246 enum class ClientIdSource { 247 // Recorded when the client ID in Local State matches the cached copy. 248 kClientIdMatches = 0, 249 // Recorded when we are somehow missing the cached client ID and we are 250 // able to recover it from the Local State. 251 kClientIdFromLocalState = 1, 252 // Recorded when we are somehow missing the client ID stored in Local State 253 // yet are able to recover it from a backup location. 254 kClientIdBackupRecovered = 2, 255 // Recorded when we are somehow missing the client ID in Local State, cache 256 // and backup and there is no provisional client ID, so a new client ID is 257 // generated. 258 kClientIdNew = 3, 259 // Recorded when we are somehow missing the client ID in Local State, cache 260 // and backup, so we promote the provisional client ID. 261 kClientIdFromProvisionalId = 4, 262 // Recorded when the client ID is passed in from external source. 263 // This is needed for Lacros since the client id is passed in from 264 // ash chrome. 265 kClientIdFromExternal = 5, 266 kMaxValue = kClientIdFromExternal, 267 }; 268 269 // Creates the MetricsStateManager with the given |local_state|. Uses 270 // |enabled_state_provider| to query whether there is consent for metrics 271 // reporting, and if it is enabled. Clients should instead use Create(), which 272 // enforces that a single instance of this class be alive at any given time. 273 // |store_client_info| should back up client info to persistent storage such 274 // that it is later retrievable by |load_client_info|. 275 MetricsStateManager(PrefService* local_state, 276 EnabledStateProvider* enabled_state_provider, 277 const std::wstring& backup_registry_key, 278 const base::FilePath& user_data_dir, 279 EntropyParams entropy_params, 280 StartupVisibility startup_visibility, 281 StoreClientInfoCallback store_client_info, 282 LoadClientInfoCallback load_client_info, 283 base::StringPiece external_client_id); 284 285 // Returns a MetricsStateManagerProvider instance and sets its 286 // |log_normal_metric_state_.gen| with the provided random seed. 287 std::unique_ptr<MetricsProvider> GetProviderAndSetRandomSeedForTesting( 288 int64_t seed); 289 290 // Backs up the current client info via |store_client_info_|. 291 void BackUpCurrentClientInfo(); 292 293 // Loads the client info via |load_client_info_|. 294 std::unique_ptr<ClientInfo> LoadClientInfo(); 295 296 // Returns the high entropy source for this client, which is composed of a 297 // client ID and the low entropy source. This is intended to be unique for 298 // each install. UMA must be enabled (and |client_id_| must be set) or 299 // |kMetricsProvisionalClientID| must be set before calling this. 300 std::string GetHighEntropySource(); 301 302 // Updates |entropy_source_returned_| with |type| iff the current value is 303 // ENTROPY_SOURCE_NONE and logs the new value in a histogram. 304 void UpdateEntropySourceReturnedValue(EntropySourceType type); 305 306 // Returns the first entropy source that was returned by this service since 307 // start up, or NONE if neither was returned yet. This is exposed for testing 308 // only. entropy_source_returned()309 EntropySourceType entropy_source_returned() const { 310 return entropy_source_returned_; 311 } 312 initial_client_id_for_testing()313 std::string initial_client_id_for_testing() const { 314 return initial_client_id_; 315 } 316 317 // Reset the client id and low entropy source if the kMetricsResetMetricIDs 318 // pref is true. 319 void ResetMetricsIDsIfNecessary(); 320 321 bool ShouldGenerateProvisionalClientId(bool is_first_run); 322 323 // Whether an instance of this class exists. Used to enforce that there aren't 324 // multiple instances of this class at a given time. 325 static bool instance_exists_; 326 327 // Weak pointer to the local state prefs store. 328 const raw_ptr<PrefService> local_state_; 329 330 // Weak pointer to an enabled state provider. Used to know whether the user 331 // has consented to reporting, and if reporting should be done. 332 raw_ptr<EnabledStateProvider> enabled_state_provider_; 333 334 // Specified options for controlling trial randomization. 335 const EntropyParams entropy_params_; 336 337 // A callback run during client id creation so this MetricsStateManager can 338 // store a backup of the newly generated ID. 339 const StoreClientInfoCallback store_client_info_; 340 341 // A callback run if this MetricsStateManager can't get the client id from 342 // its typical location and wants to attempt loading it from this backup. 343 const LoadClientInfoCallback load_client_info_; 344 345 // A beacon used to determine whether the previous Chrome browser session 346 // terminated gracefully. 347 CleanExitBeacon clean_exit_beacon_; 348 349 // The identifier that's sent to the server with the log reports. 350 std::string client_id_; 351 352 // The client id that was used do field trial randomization. This field should 353 // only be changed when we need to do group assignment. |initial_client_id| 354 // should left blank iff a client id was not used to do field trial 355 // randomization. 356 std::string initial_client_id_; 357 358 // If not empty, use an external client id passed in from another browser as 359 // |client_id_|. This is needed for the Lacros browser where client id needs 360 // be passed in from ash chrome. 361 std::string external_client_id_; 362 363 // An instance of EntropyState for getting the entropy source values. 364 EntropyState entropy_state_; 365 366 // The last entropy source returned by this service, used for testing. 367 EntropySourceType entropy_source_returned_; 368 369 // The value of prefs::kMetricsResetIds seen upon startup, i.e., the value 370 // that was appropriate in the previous session. Used when reporting previous 371 // session (stability) data. 372 bool metrics_ids_were_reset_; 373 374 // The value of the metrics id before reseting. Only possibly valid if the 375 // metrics id was reset. May be blank if the metrics id was reset but Chrome 376 // has no record of what the previous metrics id was. 377 std::string previous_client_id_; 378 379 // The detector for understanding the cloned nature of the install so that we 380 // can reset client ids. 381 ClonedInstallDetector cloned_install_detector_; 382 383 // The type of session, e.g. a foreground session, at startup. This value is 384 // used only during startup. On Android WebLayer, Android WebView, and iOS, 385 // the visibility is unknown at this point in startup. 386 const StartupVisibility startup_visibility_; 387 388 // Force enables the creation of a provisional client ID on first run even if 389 // this is not a Chrome-branded build. Used for testing. 390 static bool enable_provisional_client_id_for_testing_; 391 }; 392 393 } // namespace metrics 394 395 #endif // COMPONENTS_METRICS_METRICS_STATE_MANAGER_H_ 396