File: glic_user_status_fetcher.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 (175 lines) | stat: -rw-r--r-- 7,479 bytes parent folder | download | duplicates (3)
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
// Copyright 2025 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_GLIC_GLIC_USER_STATUS_FETCHER_H_
#define CHROME_BROWSER_GLIC_GLIC_USER_STATUS_FETCHER_H_

#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/scoped_observation.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "base/timer/wall_clock_timer.h"
#include "chrome/browser/glic/glic_user_status_code.h"
#include "chrome/browser/profiles/profile.h"
#include "components/prefs/pref_change_registrar.h"
#include "components/signin/public/base/gaia_id_hash.h"
#include "components/signin/public/identity_manager/account_managed_status_finder.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "google_apis/common/request_sender.h"
#include "google_apis/gaia/google_service_auth_error.h"

namespace glic {

namespace prefs {
enum class SettingsPolicyState;
}

// This class, GlicUserStatusFetcher, is responsible for asynchronously fetching
// the Glic user status from a Google-owned API. The response is processed in a
// callback and determines if the current signed-in primary account user is able
// to see Glic UI surfaces(e.g. tab strip button).
//
// Ownership:
// This class is instantiate as a member of the `GlicEnabling` object if the
// feature is enabled.
//
// Request Schedule:
// The fetching of the user status is scheduled as follows:
// - Upon construction: An initial request is made if enough time has passed
//   since the last successful update (based on `kGlicUserStatusRequestDelay`
//   feature flag).
// - Upon primary account sign-in: When a primary account is signed in (or when
//   Chrome launches with a signed-in primary account), a request is initiated.
// - When refresh token becomes available: If a request is attempted
//   but the refresh token is not yet available, the fetch is retried when the
//   refresh token is updated.
// - Periodically: Subsequent requests are scheduled to occur every
//   `kGlicUserStatusRequestDelay` (currently 23 hours) after a successful
//   response.
// See comments on the timer members for more info.
//
// Error Handling:
// If the server responds with an HTTP error (non-200 status code) or fails to
// provide a valid JSON response, the `ProcessResponse` method will interpret
// this as `UserStatusCode::SERVER_UNAVAILABLE`. In this scenario, the locally
// cached user status (if any) is *not* overwritten, ensuring that the Glic
// enabling state remains consistent based on the last known good status. The
// callback will still be executed to notify the observer of the fetch attempt
// outcome.
//
// Clock Changes:
// The `GlicUserStatusFetcher` use the `base::WallClockTimer` for
// scheduling the periodic fetching. Unlike, `base::TimeTicks`, `base::Time`
// which `base::WallClockTimer` uses does not freeze during suspend. Significant
// system clock errors could lead to unexpected timing of the status updates and
// potentially affect the QPS too. However, we expect it is only minor system
// clock adjustment if any.
class GlicUserStatusFetcher : public signin::IdentityManager::Observer {
 public:
  using FetchOverrideCallback = base::RepeatingCallback<void(
      base::OnceCallback<void(const CachedUserStatus&)>)>;

  explicit GlicUserStatusFetcher(Profile* profile,
                                 base::RepeatingClosure callback);
  ~GlicUserStatusFetcher() override;

  static std::optional<CachedUserStatus> GetCachedUserStatus(Profile* profile);

  void InvalidateCachedStatus();
  void UpdateUserStatus();
  void UpdateUserStatusIfNeeded();
  void ScheduleUserStatusUpdate(base::TimeDelta time_to_next_update);
  void CancelUserStatusUpdateIfNeeded();

  // Updates the user status when information suggests that it might have
  // changed recently. This is internally throttled to avoid excessive
  // requests, for signals that might be received multiple times.
  void UpdateUserStatusWithThrottling();

  void SetGlicUserStatusUrlForTest(GURL test_url) { endpoint_ = test_url; }
  void SetFetchOverrideForTest(FetchOverrideCallback fetch_override) {
    fetch_override_for_test_ = std::move(fetch_override);
  }

 private:
  static std::optional<signin::GaiaIdHash> GetGaiaIdHashForPrimaryAccount(
      Profile* profile);

  // Updates the user status and schedules another update in the future.
  void UpdateUserStatusAndScheduleNextRefresh();

  // Called when the account managed status is found, if it was not available
  // when `UpdateUserStatus` was called.
  void OnAccountManagedStatusFound();

  // signin::IdentityManager::Observer:
  void OnPrimaryAccountChanged(
      const signin::PrimaryAccountChangeEvent& event_details) override;
  void OnRefreshTokenUpdatedForAccount(
      const CoreAccountInfo& account_info) override;
  void OnErrorStateOfRefreshTokenUpdatedForAccount(
      const CoreAccountInfo& account_info,
      const GoogleServiceAuthError& error,
      signin_metrics::SourceForRefreshTokenOperation token_operation_source)
      override;
  void OnIdentityManagerShutdown(
      signin::IdentityManager* identity_manager) override;

  // Called when the Gemini settings pref changes.
  void OnGeminiSettingsChanged();

  void ProcessResponse(const std::string& account_id_hash,
                       const CachedUserStatus& user_status);

  // If true, the user status update could not proceed because the refresh token
  // was not yet available, and it should be retried when it becomes available.
  bool is_user_status_waiting_for_refresh_token_ = false;

  // Stores the previous value of `prefs::kGeminiSettings` to detect
  // transitions.
  glic::prefs::SettingsPolicyState cached_gemini_settings_value_;

  // Used to find the account managed status of the primary account.
  // A finder will exist only if the status is pending.
  std::unique_ptr<signin::AccountManagedStatusFinder>
      account_managed_status_finder_;
  signin::AccountManagedStatusFinderOutcome account_managed_status_ =
      signin::AccountManagedStatusFinderOutcome::kPending;

  raw_ptr<Profile> profile_;
  const base::RepeatingClosure callback_;
  GURL endpoint_;
  std::string oauth2_scope_;

  // Ensures we run a request at least as often as
  // `features::kGlicUserStatusRequestDelay`. Reset on browser start, when a
  // periodic refresh is running, and when any request (periodic or otherwise)
  // completes successfully.
  base::WallClockTimer refresh_status_timer_;

  // Ensures certain events which might trigger sooner refreshes don't occur
  // more often than every `features::kGlicUserStatusThrottleInterval`.
  base::OneShotTimer throttle_timer_;

  // If true, a throttled update was requested too soon after the last one.
  // Always false when `throttle_timer_` is not running.
  bool update_was_throttled_ = false;

  std::unique_ptr<google_apis::RequestSender> request_sender_;
  base::OnceClosure cancel_closure_;

  // When set, replaces the actual fetch with a callback.
  FetchOverrideCallback fetch_override_for_test_;

  base::ScopedObservation<signin::IdentityManager,
                          signin::IdentityManager::Observer>
      identity_manager_observation_{this};
  PrefChangeRegistrar pref_change_registrar_;

  base::WeakPtrFactory<GlicUserStatusFetcher> weak_ptr_factory_{this};
};

}  // namespace glic
#endif  // CHROME_BROWSER_GLIC_GLIC_USER_STATUS_FETCHER_H_