xref: /aosp_15_r20/external/cronet/components/metrics/clean_exit_beacon.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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_CLEAN_EXIT_BEACON_H_
6 #define COMPONENTS_METRICS_CLEAN_EXIT_BEACON_H_
7 
8 #include <optional>
9 #include <string>
10 
11 #include "base/files/file_path.h"
12 #include "base/memory/raw_ptr.h"
13 #include "base/time/time.h"
14 #include "base/values.h"
15 #include "build/build_config.h"
16 
17 class PrefRegistrySimple;
18 class PrefService;
19 
20 namespace metrics {
21 
22 // The name of the beacon file, which is relative to the user data directory
23 // and used to store the CleanExitBeacon value and the variations crash streak.
24 extern const base::FilePath::CharType kCleanExitBeaconFilename[];
25 
26 // Captures all possible beacon value permutations for two distinct beacons.
27 // Exposed for testing.
28 //
29 // These values are persisted to logs. Entries should not be renumbered and
30 // numeric values should never be reused.
31 enum class CleanExitBeaconConsistency {
32   kCleanClean = 0,
33   kCleanDirty = 1,
34   kCleanMissing = 2,
35   kDirtyClean = 3,
36   kDirtyDirty = 4,
37   kDirtyMissing = 5,
38   kMissingClean = 6,
39   kMissingDirty = 7,
40   kMissingMissing = 8,
41   kMaxValue = kMissingMissing,
42 };
43 
44 // Denotes the state of the beacon file. Exposed for testing.
45 //
46 // These values are persisted to logs. Entries should not be renumbered and
47 // numeric values should never be reused.
48 enum class BeaconFileState {
49   kReadable = 0,
50   kNotDeserializable = 1,
51   kMissingDictionary = 2,
52   kMissingCrashStreak = 3,
53   kMissingBeacon = 4,
54   kMaxValue = kMissingBeacon,
55 };
56 
57 // Reads and updates a beacon used to detect whether the previous browser
58 // process exited cleanly.
59 class CleanExitBeacon {
60  public:
61   // Instantiates a CleanExitBeacon whose value is stored in
62   // |has_exited_cleanly_|. The value is persisted in the beacon file on
63   // platforms that support this mechanism and in Local State on platforms that
64   // don't.
65   //
66   // On Windows, |backup_registry_key| stores a backup of the beacon to verify
67   // that the pref's value corresponds to the registry's. |backup_registry_key|
68   // is ignored on other platforms, but iOS has a similar verification
69   // mechanism embedded inside CleanExitBeacon.
70   //
71   // |user_data_dir| is the path to the client's user data directory. If empty,
72   // the beacon file is not used.
73   CleanExitBeacon(const std::wstring& backup_registry_key,
74                   const base::FilePath& user_data_dir,
75                   PrefService* local_state);
76 
77   virtual ~CleanExitBeacon() = default;
78 
79   // Not copyable or movable.
80   CleanExitBeacon(const CleanExitBeacon&) = delete;
81   CleanExitBeacon& operator=(const CleanExitBeacon&) = delete;
82 
83   // Initializes the CleanExitBeacon. This includes the following tasks:
84   // 1. Determining if the last session exited cleanly,
85   // 2. Incrementing the crash streak, if necessary, and
86   // 3. Emitting some metrics.
87   void Initialize();
88 
89   // Returns the original value of the beacon.
exited_cleanly()90   bool exited_cleanly() const { return did_previous_session_exit_cleanly_; }
91 
92   // Returns the original value of the last live timestamp.
browser_last_live_timestamp()93   base::Time browser_last_live_timestamp() const {
94     return initial_browser_last_live_timestamp_;
95   }
96 
97   // Returns true if Extended Variations Safe Mode is supported on this
98   // platform. Android WebLayer and WebView do not support this.
99   bool IsExtendedSafeModeSupported() const;
100 
101   // Sets the beacon value to |exited_cleanly| and writes the value to disk if
102   // the current value (see has_exited_cleanly_) is not already
103   // |exited_cleanly|. Note that on platforms that do not support the beacon
104   // file, the write is scheduled, so the value may not be persisted if the
105   // browser process crashes.
106   //
107   // Also, updates the last live timestamp.
108   //
109   // |is_extended_safe_mode| denotes whether Chrome is about to start watching
110   // for browser crashes early on in startup as a part of Extended Variations
111   // Safe Mode, which is supported by most, but not all, platforms.
112   //
113   // TODO(crbug.com/40850854): Consider removing |is_extended_safe_mode|.
114   void WriteBeaconValue(bool exited_cleanly,
115                         bool is_extended_safe_mode = false);
116 
117   // Updates the last live timestamp.
118   void UpdateLastLiveTimestamp();
119 
120   const base::FilePath GetUserDataDirForTesting() const;
121   base::FilePath GetBeaconFilePathForTesting() const;
122 
123   // Registers local state prefs used by this class.
124   static void RegisterPrefs(PrefRegistrySimple* registry);
125 
126   // Updates both Local State and NSUserDefaults beacon values.
127   static void SetStabilityExitedCleanlyForTesting(PrefService* local_state,
128                                                   bool exited_cleanly);
129 
130   // Creates and returns a well-formed beacon file contents with the given
131   // values.
132   static std::string CreateBeaconFileContentsForTesting(bool exited_cleanly,
133                                                         int crash_streak);
134 
135   // Resets both Local State and NSUserDefaults beacon values.
136   static void ResetStabilityExitedCleanlyForTesting(PrefService* local_state);
137 
138   // CHECKs that Chrome exited cleanly.
139   static void EnsureCleanShutdown(PrefService* local_state);
140 
141 #if BUILDFLAG(IS_IOS)
142   // Sets the NSUserDefaults beacon value.
143   static void SetUserDefaultsBeacon(bool exited_cleanly);
144 
145   // Checks user default value of kUseUserDefaultsForExitedCleanlyBeacon.
146   // Because variations are not initialized early in startup, pair a user
147   // defaults value with the variations config.
148   static bool ShouldUseUserDefaultsBeacon();
149 
150   // Syncs feature kUseUserDefaultsForExitedCleanlyBeacon to NSUserDefaults
151   // kUserDefaultsFeatureFlagForExitedCleanlyBeacon.
152   static void SyncUseUserDefaultsBeacon();
153 #endif  // BUILDFLAG(IS_IOS)
154 
155   // Prevents a test browser from performing two clean shutdown steps. First, it
156   // prevents the beacon value from being updated after this function is called.
157   // This prevents the the test browser from signaling that Chrome is shutting
158   // down cleanly. Second, it makes EnsureCleanShutdown() a no-op.
159   static void SkipCleanShutdownStepsForTesting();
160 
161  private:
162   // Returns true if the previous session exited cleanly. Either Local State
163   // or |beacon_file_contents| is used to get this information. Which is used
164   // depends on the client's platform and the existence of a valid beacon file.
165   // Also, records several metrics.
166   //
167   // Should be called only once: at startup.
168   bool DidPreviousSessionExitCleanly(base::Value* beacon_file_contents);
169 
170   // Returns true if the beacon file is supported on this platform. Android
171   // WebLayer and WebView do not support this.
172   bool IsBeaconFileSupported() const;
173 
174   // Writes |exited_cleanly| and the crash streak to the file located at
175   // |beacon_file_path_|.
176   void WriteBeaconFile(bool exited_cleanly) const;
177 
178 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_IOS)
179   // Returns whether Chrome exited cleanly in the previous session according to
180   // the platform-specific beacon (the registry for Windows or NSUserDefaults
181   // for iOS). Returns std::nullopt if the platform-specific location does not
182   // have beacon info.
183   std::optional<bool> ExitedCleanly();
184 #endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_IOS)
185 
186 #if BUILDFLAG(IS_IOS)
187   // Returns true if the NSUserDefaults beacon value is set.
188   static bool HasUserDefaultsBeacon();
189 
190   // Returns the NSUserDefaults beacon value.
191   static bool GetUserDefaultsBeacon();
192 
193   // Clears the NSUserDefaults beacon value.
194   static void ResetUserDefaultsBeacon();
195 #endif  // BUILDFLAG(IS_IOS)
196 
197   // Indicates whether the CleanExitBeacon has been initialized.
198   bool initialized_ = false;
199 
200   // Stores a backup of the beacon. Windows only.
201   const std::wstring backup_registry_key_;
202 
203   // Path to the client's user data directory. May be empty.
204   const base::FilePath user_data_dir_;
205 
206   const raw_ptr<PrefService> local_state_;
207 
208   // This is the value of the last live timestamp from local state at the time
209   // of construction. It is a timestamp from the previous browser session when
210   // the browser was known to be alive.
211   const base::Time initial_browser_last_live_timestamp_;
212 
213   bool did_previous_session_exit_cleanly_ = false;
214 
215   // Denotes the current beacon value for this session, which is updated via
216   // CleanExitBeacon::WriteBeaconValue(). When `false`, Chrome is watching for
217   // browser crashes. When `true`, Chrome has stopped watching for crashes. When
218   // unset, Chrome has neither started nor stopped watching for crashes.
219   std::optional<bool> has_exited_cleanly_ = std::nullopt;
220 
221   // Where the clean exit beacon and the variations crash streak are stored on
222   // platforms that support the beacon file.
223   base::FilePath beacon_file_path_;
224 };
225 
226 }  // namespace metrics
227 
228 #endif  // COMPONENTS_METRICS_CLEAN_EXIT_BEACON_H_
229