File: upgrade_detector.h

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (439 lines) | stat: -rw-r--r-- 16,588 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
// Copyright 2011 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_UPGRADE_DETECTOR_UPGRADE_DETECTOR_H_
#define CHROME_BROWSER_UPGRADE_DETECTOR_UPGRADE_DETECTOR_H_

#include <optional>
#include <string>

#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/sequence_checker.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "build/build_config.h"
#include "chrome/browser/upgrade_detector/upgrade_observer.h"
#include "components/prefs/pref_change_registrar.h"

class PrefRegistrySimple;
class UpgradeObserver;
namespace base {
class Clock;
class TickClock;
}

///////////////////////////////////////////////////////////////////////////////
// UpgradeDetector
//
// This class is a singleton class that monitors when an upgrade happens in the
// background. We basically ask Omaha what it thinks the latest version is and
// if our version is lower we send out a notification upon:
//   a) Detecting an upgrade and...
//   b) When we think the user should be notified about the upgrade.
// The latter happens much later, since we don't want to be too annoying.
// This class is not thread safe -- all access must take place on the UI thread.
//
class UpgradeDetector {
 public:
  // The Homeland Security Upgrade Advisory System.
  // These values are persisted to logs. Entries should not be renumbered and
  // numeric values should never be reused.
  enum UpgradeNotificationAnnoyanceLevel {
    UPGRADE_ANNOYANCE_NONE = 0,      // What? Me worry?
    UPGRADE_ANNOYANCE_LOW = 1,       // Green.
    UPGRADE_ANNOYANCE_ELEVATED = 2,  // Yellow.
    UPGRADE_ANNOYANCE_HIGH = 3,      // Red.
    // UPGRADE_ANNOYANCE_SEVERE = 4,  // Removed in 2018-03 for lack of use.
    UPGRADE_ANNOYANCE_CRITICAL = 5,  // Red exclamation mark.
    UPGRADE_ANNOYANCE_VERY_LOW = 6,  // Green early warning for canary and dev.
    UPGRADE_ANNOYANCE_GRACE = 7,     // Red last warning before deadline.
    UPGRADE_ANNOYANCE_MAX_VALUE = UPGRADE_ANNOYANCE_GRACE
  };

  struct RelaunchWindow {
    constexpr RelaunchWindow(int start_hour,
                             int start_minute,
                             base::TimeDelta duration)
        : hour(start_hour), minute(start_minute), duration(duration) {}

    bool IsValid() const {
      return hour >= 0 && hour <= 23 && minute >= 0 && minute <= 59 &&
             duration >= base::Minutes(1) && duration != base::TimeDelta::Max();
    }

    int hour;
    int minute;
    base::TimeDelta duration;
  };

  // Returns the singleton implementation instance.
  static UpgradeDetector* GetInstance();

  UpgradeDetector(const UpgradeDetector&) = delete;
  UpgradeDetector& operator=(const UpgradeDetector&) = delete;

  virtual ~UpgradeDetector();

  // Returns the default delta from upgrade detection until high annoyance is
  // reached.
  static base::TimeDelta GetDefaultHighAnnoyanceThreshold();

  // Returns the default delta from upgrade detection until elevated annoyance
  // is reached.
  static base::TimeDelta GetDefaultElevatedAnnoyanceThreshold();

  static void RegisterPrefs(PrefRegistrySimple* registry);

  virtual void Init();
  virtual void Shutdown();

  // Returns the time at which an available upgrade was detected.
  base::Time upgrade_detected_time() const {
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    return upgrade_detected_time_;
  }

  // Whether the user should be notified about an upgrade.
  bool notify_upgrade() const {
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    return notify_upgrade_;
  }

  // Whether the upgrade recommendation is due to Chrome being outdated.
  bool is_outdated_install() const {
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    return upgrade_available_ == UPGRADE_NEEDED_OUTDATED_INSTALL;
  }

  // Whether the upgrade recommendation is due to Chrome being outdated AND
  // auto-update is turned off.
  bool is_outdated_install_no_au() const {
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    return upgrade_available_ == UPGRADE_NEEDED_OUTDATED_INSTALL_NO_AU;
  }

  // Returns true if the detector has found that a newer version of Chrome is
  // installed and a relaunch would complete the update.
  bool is_upgrade_available() const {
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    return upgrade_available_ == UPGRADE_AVAILABLE_REGULAR ||
           upgrade_available_ == UPGRADE_AVAILABLE_CRITICAL;
  }

  // Notify this object that the user has acknowledged the critical update so we
  // don't need to complain about it for now.
  void acknowledge_critical_update() {
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    critical_update_acknowledged_ = true;
  }

  // Whether the user has acknowledged the critical update.
  bool critical_update_acknowledged() const {
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    return critical_update_acknowledged_;
  }

#if BUILDFLAG(IS_CHROMEOS)
  bool is_factory_reset_required() const {
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    return is_factory_reset_required_;
  }

  bool is_rollback() const {
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    return is_rollback_;
  }
#endif  // BUILDFLAG(IS_CHROMEOS)

  UpgradeNotificationAnnoyanceLevel upgrade_notification_stage() const {
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    return upgrade_notification_stage_;
  }

  // Returns the time at which `level` annoyance level will be (or was) reached,
  // or a null time object if an upgrade has not yet been detected.
  virtual base::Time GetAnnoyanceLevelDeadline(
      UpgradeNotificationAnnoyanceLevel level) = 0;

  // Overrides the "high" annoyance deadline, setting it to |deadline|. On
  // Chrome OS, this also sets the "elevated" annoyance deadline to the time at
  // which the available update was detected. This has no effect on desktop
  // Chrome browsers.
  virtual void OverrideHighAnnoyanceDeadline(base::Time deadine) {}

  // Resets the overridden deadlines and recalculates them according to the
  // thresholds from the Local State. This has no effect on desktop Chrome
  // browsers.
  virtual void ResetOverriddenDeadline() {}

  // Overrides the relaunch notification style to required if |override|; else
  // resets the override so that the policy settings take effect.
  void OverrideRelaunchNotificationToRequired(bool overridden);

  void AddObserver(UpgradeObserver* observer);

  void RemoveObserver(UpgradeObserver* observer);

  // Notifies that the current install is outdated. No details are expected.
  void NotifyOutdatedInstall();

  // Notifies that the current install is outdated and auto-update (AU) is
  // disabled. No details are expected.
  void NotifyOutdatedInstallNoAutoUpdate();

  void set_upgrade_notification_stage_for_testing(
      UpgradeNotificationAnnoyanceLevel stage) {
    set_upgrade_notification_stage(stage);
  }

  void NotifyUpgradeForTesting();

 protected:
  enum UpgradeAvailable {
    // If no update is available and current install is recent enough.
    UPGRADE_AVAILABLE_NONE,
    // If a regular update is available.
    UPGRADE_AVAILABLE_REGULAR,
    // If a critical update to Chrome has been installed, such as a zero-day
    // fix.
    UPGRADE_AVAILABLE_CRITICAL,
    // If no update to Chrome has been installed for more than the recommended
    // time.
    UPGRADE_NEEDED_OUTDATED_INSTALL,
    // If no update to Chrome has been installed for more than the recommended
    // time AND auto-update is turned off.
    UPGRADE_NEEDED_OUTDATED_INSTALL_NO_AU,
  };

  UpgradeDetector(const base::Clock* clock, const base::TickClock* tick_clock);

  // Starts observing changes to Local State preference `pref`.
  void MonitorPrefChanges(const std::string& pref);

  // Returns the notification period specified via the
  // RelaunchNotificationPeriod policy setting, or a zero delta if unset or out
  // of range.
  static base::TimeDelta GetRelaunchNotificationPeriod();
  static bool IsRelaunchNotificationPolicyEnabled();

  // Returns the adjusted deadline to fall within `window`. If the
  // `deadline` has already passed the window for the day, it is prolonged for
  // the next day within the window. If the `deadline` already falls within the
  // window, no change is made.
  static base::Time AdjustDeadline(base::Time deadline,
                                   const RelaunchWindow& window);

  // Returns the relaunch window specified via the RelaunchWindow policy
  // setting, or nullopt if unset or set incorrectly.
  static std::optional<RelaunchWindow> GetRelaunchWindowPolicyValue();

  // Returns the default relaunch window within which the relaunch should take
  // place. It is 2am to 4am from Chrome OS and the whole day for others.
  static RelaunchWindow GetDefaultRelaunchWindow();

  // Returns the delta between "grace" and "high" annoyance levels using
  // `elevated_to_high_delta` which is the delta between "elevated" and "high"
  // annoyance levels.
  static base::TimeDelta GetGracePeriod(base::TimeDelta elevated_to_high_delta);

  // Returns the network time, falling back to system time if unavailable.
  // Returns true if it's network time, or false if it's not.
  bool GetNetworkTimeWithFallback(base::Time& network_time);

  // Returns true if `last_served_date_` is known and older than the number
  // of days specified by the RelaunchFastIfOutdated policy.
  bool ShouldRelaunchFast();

  // Returns true if the last served date should be fetched on update, for
  // the RelaunchOutdatedInstall policy.
  bool ShouldFetchLastServedDate() const;

  // Fetches the last served date via GetLastServedDate(), and updates
  // `last_served_date_`.
  void FetchLastServedDate();

  const base::Clock* clock() {
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    return clock_;
  }

  const base::TickClock* tick_clock() {
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    return tick_clock_;
  }

  // Notifies that update is recommended and triggers different actions based
  // on the update availability.
  void NotifyUpgrade();

  // Notifies that update is recommended.
  void NotifyUpgradeRecommended();

  // Notifies that a critical update has been installed. No details are
  // expected.
  void NotifyCriticalUpgradeInstalled();

  // Notifies that an update is downloaded but deferred. Set `use_notification`
  // to true to enable system tray notification.
  void NotifyUpdateDeferred(bool use_notification);

  // The function that sends out a notification that lets the rest of the UI
  // know we should notify the user that a new update is available to download
  // over cellular connection.
  void NotifyUpdateOverCellularAvailable();

  // Notifies that the user's one time permission on update over cellular
  // connection has been granted.
  void NotifyUpdateOverCellularOneTimePermissionGranted();

  // Notifies about a request to override the relaunch notification style to
  // required or reset the overridden style.
  void NotifyRelaunchOverriddenToRequired(bool overridden);

  // Triggers a critical update, which starts a timer that checks the machine
  // idle state. Protected and virtual so that it could be overridden by tests.
  virtual void TriggerCriticalUpdate();

  UpgradeAvailable upgrade_available() const {
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    return upgrade_available_;
  }
  void set_upgrade_available(UpgradeAvailable available) {
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    upgrade_available_ = available;
  }

  void set_upgrade_detected_time(base::Time upgrade_detected_time) {
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    upgrade_detected_time_ = upgrade_detected_time;
  }

  void set_best_effort_experiment_updates_available(bool available) {
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    best_effort_experiment_updates_available_ = available;
  }

  bool critical_experiment_updates_available() const {
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    return critical_experiment_updates_available_;
  }
  void set_critical_experiment_updates_available(bool available) {
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    critical_experiment_updates_available_ = available;
  }

  void set_critical_update_acknowledged(bool acknowledged) {
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    critical_update_acknowledged_ = acknowledged;
  }

  void set_upgrade_notification_stage(UpgradeNotificationAnnoyanceLevel stage) {
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    upgrade_notification_stage_ = stage;
  }

#if BUILDFLAG(IS_CHROMEOS)
  void set_is_factory_reset_required(bool is_factory_reset_required) {
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    is_factory_reset_required_ = is_factory_reset_required;
  }

  void set_is_rollback(bool is_rollback) {
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    is_rollback_ = is_rollback;
  }
#endif  // BUILDFLAG(IS_CHROMEOS)

 private:
  FRIEND_TEST_ALL_PREFIXES(AppMenuModelTest, Basics);
  FRIEND_TEST_ALL_PREFIXES(RelaunchNotificationControllerUiTest,
                           ReactivateAfterDeadline);
  FRIEND_TEST_ALL_PREFIXES(SystemTrayClientTest, UpdateTrayIcon);
  friend class RelaunchNotificationControllerUiTest;
  friend class UpgradeMetricsProviderTest;

  // Called on the UI thread after one or more monitored prefs or
  // `last_served_date_` have changed. If an update has been detected,
  // subclasses may need to recompute the schedule for advancing through the
  // annoyance levels.
  virtual void RecomputeSchedule() {}

  // Initiates an Idle check. Tells us whether Chrome has received any
  // input events since the specified time.
  void CheckIdle();

  // Handles a change to the relaunch notification related Local State
  // preferences. Posts a task to call OnThresholdPrefChanged() if it isn't
  // already posted and pending for execution.
  void OnRelaunchPrefChanged();

  // Handles the result of GetLastServedDate().
  void OnGotLastServedDate(std::optional<base::Time> last_served_date);

  // A provider of Time to the detector.
  const raw_ptr<const base::Clock> clock_;

  // A provider of TimeTicks to the detectors' timers.
  const raw_ptr<const base::TickClock> tick_clock_;

  // Observes changes to the browser.relaunch_notification_period Local State
  // preference.
  PrefChangeRegistrar pref_change_registrar_;

  // Whether any software updates are available (experiment updates are tracked
  // separately via additional member variables below).
  UpgradeAvailable upgrade_available_;

  // The time at which an available upgrade was detected.
  base::Time upgrade_detected_time_;

  // Whether "best effort" experiment updates are available.
  bool best_effort_experiment_updates_available_;

  // Whether "critical" experiment updates are available.
  bool critical_experiment_updates_available_;

  // Whether the user has acknowledged the critical update.
  bool critical_update_acknowledged_;

  // Whether a task posted on any relaunch preference change is still pending
  // for execution.
  bool pref_change_task_pending_ = false;

#if BUILDFLAG(IS_CHROMEOS)
  // Whether a factory reset is needed to complete an update.
  bool is_factory_reset_required_ = false;

  // Whether the update is actually an admin-initiated rollback of the device
  // to an earlier version of Chrome OS, which results in the device being
  // wiped when it's rebooted.
  bool is_rollback_ = false;
#endif  // BUILDFLAG(IS_CHROMEOS)

  // A timer to check to see if we've been idle for long enough to show the
  // critical warning. Should only be set if |upgrade_available_| is
  // UPGRADE_AVAILABLE_CRITICAL.
  base::RepeatingTimer idle_check_timer_;

  // The stage at which the annoyance level for upgrade notifications is at.
  UpgradeNotificationAnnoyanceLevel upgrade_notification_stage_;

  // Whether we have waited long enough after detecting an upgrade (to see
  // is we should start nagging about upgrading).
  bool notify_upgrade_;

  bool fetched_last_served_date_ = false;
  std::optional<base::Time> last_served_date_;

  SEQUENCE_CHECKER(sequence_checker_);

  base::ObserverList<UpgradeObserver>::Unchecked observer_list_;

  base::WeakPtrFactory<UpgradeDetector> weak_factory_{this};
};

#endif  // CHROME_BROWSER_UPGRADE_DETECTOR_UPGRADE_DETECTOR_H_