Switch global error menu icon to vectorized MD asset
[chromium-blink-merge.git] / chrome / browser / signin / cross_device_promo.h
blob5b1f5c5984ba186efe93e46d5a45d4b9343f4ef5
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #ifndef CHROME_BROWSER_SIGNIN_CROSS_DEVICE_PROMO_H_
6 #define CHROME_BROWSER_SIGNIN_CROSS_DEVICE_PROMO_H_
8 #include "base/observer_list.h"
9 #include "base/timer/timer.h"
10 #include "components/keyed_service/core/keyed_service.h"
11 #include "components/signin/core/browser/device_activity_fetcher.h"
12 #include "components/signin/core/browser/gaia_cookie_manager_service.h"
14 class PrefService;
15 class SigninClient;
16 class SigninManager;
18 // The Cross Device Promo promotes Chrome Signin within a profile where there is
19 // one GAIA account signed in to the content area for a long period of time
20 // (indicating that this profile is used by only one user) and where that
21 // account uses Chrome Sync on other devices but is not signed in here.
23 // This class determines whether the above criteria have been met and thus
24 // whether the promo should be displayed. This class imposes additional criteria
25 // such that the promo is only displayed if there is sufficiently recent
26 // activity on another device, indicating the user is switching contexts but
27 // continuing their browsing activity. This class's behavior is controlled by
28 // the "CrossDevicePromo" field trial.
29 // The UI is defined elsewhere.
31 // This class implements the preferences necessary to track whether a profile
32 // meets the above criteria, and whether the user has permanently opted out of
33 // the promo.
35 // The class relies on the GaiaCookieManagerService to determine if there has
36 // been a single GAIA account signed in to the content area for a long period of
37 // time, and relies on the DeviceActivityFetcher to determine if that account
38 // uses Chrome Sync on other devices.
39 class CrossDevicePromo : public KeyedService,
40 public GaiaCookieManagerService::Observer,
41 public DeviceActivityFetcher::Observer {
42 public:
43 class Observer {
44 public:
45 // Called when the profile moves from being ineligible to eligible for the
46 // promo or vice versa; the new state is noted in the |eligible| parameter.
47 virtual void OnPromoEligibilityChanged(bool eligible) = 0;
50 // The following constants are the parameters for a particular experiment
51 // within the field trial that controls this class's behaviors.
52 static const char kCrossDevicePromoFieldTrial[];
53 // This field trial parameter specifies how often the device activity should
54 // be fetched.
55 static const char kParamHoursBetweenDeviceActivityChecks[];
56 // This field trial parameter defines for how long the profile's cookie must
57 // contain exactly one GAIA account before the profile is considered
58 // single-user.
59 static const char kParamDaysToVerifySingleUserProfile[];
60 // This field trial parameter defines how much time must pass between calls
61 // to MaybeBrowsingSessionStarted() before the code considers a new browsing
62 // session to have started and re-evaluates if the promo should be shown.
63 static const char kParamMinutesBetweenBrowsingSessions[];
64 // This field trial parameter defines how much time may pass between the last
65 // observed device activity and the start of a new browsing session for the
66 // promo to consider the new browsing session to be a context switch.
67 static const char kParamMinutesMaxContextSwitchDuration[];
68 // This field trial parameter defines what percentage of all device activity
69 // fetches should be not be executed, to throttle requests to the server.
70 static const char kParamRPCThrottle[];
72 // Constructor takes non-null pointers to required services. This object does
73 // not take ownership of any of the passed objects. This also calls Init().
74 explicit CrossDevicePromo(SigninManager* signin_manager,
75 GaiaCookieManagerService* cookie_manager_service,
76 SigninClient* signin_client,
77 PrefService* pref_service);
78 ~CrossDevicePromo() override;
80 // KeyedService:
81 // Ends observation of other services and records the length of any current
82 // browsing session (see signin_metrics::LogBrowsingSessionDuration()). This
83 // is called only during Chrome shutdown.
84 void Shutdown() override;
86 // GaiaCookieManagerService::Observer:
87 // This supports monitoring whether the content area is signed into exactly
88 // one GAIA account for a long period of time. This tracks the earliest time
89 // |accounts| contained (and still contains) exactly one account, so that
90 // other methods can use kParamDaysToVerifySingleUserProfile to verify if this
91 // Profile is considered single-user.
92 void OnGaiaAccountsInCookieUpdated(
93 const std::vector<gaia::ListedAccount>& accounts,
94 const GoogleServiceAuthError& error) override;
96 // DeviceActivityFetcher::Observer:
97 // OnFetchDeviceActivity* are called from |device_activity_fetcher_| which
98 // was created in GetDevicesActivityForGAIAAccountInCookieJar(). Deletes
99 // |device_activity_fetcher_| at the end of the method.
100 // See DetermineEligibilityFromDeviceActivity() for details.
101 void OnFetchDeviceActivitySuccess(
102 const std::vector<DeviceActivityFetcher::DeviceActivity>& devices)
103 override;
104 void OnFetchDeviceActivityFailure() override;
106 // Callable by third parties to register or unregister for callbacks when the
107 // promo's eligibility-to-be-shown state changes.
108 void AddObserver(CrossDevicePromo::Observer* observer);
109 void RemoveObserver(CrossDevicePromo::Observer* observer);
111 // Returns whether the profile has been marked as eligible to be shown the
112 // promo.
113 bool ShouldShowPromo() const;
115 // Called when the user requests to opt out of the promo. This will set a pref
116 // that forever marks the profile ineligible for the promo.
117 void OptOut();
119 // Called whenever a browser becomes active. Notes the start of a new browsing
120 // session if the last call to this method (noted in |previous_last_active|)
121 // was more than |inactivity_between_browsing_sessions_| ago. For new browsing
122 // sessions, will either determine if the promo is eligible to be shown, or
123 // will use |device_activity_timer_| to schedule getting more information with
124 // GetDevicesActivityForGAIAAccountInCookieJar().
125 void MaybeBrowsingSessionStarted(const base::Time& previous_last_active);
127 // Called only in tests; calls Init() if not already initialized. See comments
128 // on |initialized_| for details.
129 bool CheckPromoEligibilityForTesting();
131 private:
132 // Initializes configuration parameters from the "CrossDevicePromo" field
133 // trial and registers for changes to the relevant GAIA cookie. In tests, this
134 // may be called more than once; see |initialized_| for details.
135 void Init();
137 // Called when the determination of whether to show the promo has been made.
138 // This both stores that decision and notifies all registered observers of any
139 // change.
140 void MarkPromoShouldBeShown();
141 void MarkPromoShouldNotBeShown();
143 // Performs all checks to determine if this profile could be shown the promo
144 // except for initiating a fetch for additional data. This will return false
145 // if the data available locally indicates the profile should not be shown
146 // the promo; returns true if the profile could be shown the promo (even if
147 // additional checks are to be performed).
148 bool CheckPromoEligibility();
150 // Called whenever new device activity is available. Checks that there is at
151 // least one device that had activity within the past
152 // kParamMinutesMaxContextSwitchDuration to determine if the promo should be
153 // shown. Once determined, the MarkPromoShould[Not]BeShown() method is called.
154 // Note that if the device is in a context switch, a followup call to
155 // GetDevicesActivityForGAIAAccountInCookieJar() will be scheduled for when
156 // the context switch would expire.
157 void DetermineEligibilityFromDeviceActivity(
158 const std::vector<DeviceActivityFetcher::DeviceActivity>& devices);
160 // Helpers to get and set the time value stored in a pref.
161 base::Time GetTimePref(const std::string& pref) const;
162 void SetTimePref(const std::string& pref, const base::Time& value);
164 // Performs checks if the promo is eligible to be displayed to this profile.
165 // This will not write any prefs or initiate any checks that are otherwise
166 // called in CheckPromoEligibility(). Records no metrics. Used for DCHECKs.
167 bool VerifyPromoEligibleReadOnly() const;
169 // Adds or removes |this| as an observer of |cookie_manager_service_|.
170 // We observe the |cookie_manager_service_| for its lifetime.
171 void RegisterForCookieChanges();
172 void UnregisterForCookieChanges();
174 // Creates a new DeviceActivityFetcher to get the list of devices, and the
175 // details of the devices (see DeviceActivityFetcher.h) where the GAIA account
176 // in this profile's cookie jar is signed in to Chrome Sync.
177 // If a |device_activity_fetcher_| is already executing a fetch, this method
178 // will not start a second fetch, as the results would be the same.
179 void GetDevicesActivityForGAIAAccountInCookieJar();
181 // Set by Init() to indicate if the promo service has been successfully
182 // initialized. Initialization will not occur if the user has previously opted
183 // out of the promo. Also, successful initialization requires all necessary
184 // parameters that control the promo to be read from the field trial.
185 // In testing an initial call to Init() may fail and a future call may succeed
186 // (see Init()); in non-test scenarios, however, failed initialization is
187 // unrecoverable and future calls to other class methods should be no-ops.
188 bool initialized_;
190 // These four pointers are weak pointers; they are not owned by this object
191 // and will outlive this object.
192 SigninManager* signin_manager_;
193 GaiaCookieManagerService* cookie_manager_service_;
194 PrefService* prefs_;
195 SigninClient* signin_client_;
197 scoped_ptr<DeviceActivityFetcher> device_activity_fetcher_;
198 base::ObserverList<CrossDevicePromo::Observer> observer_list_;
200 // Initialized from the |kParamMinutesMaxContextSwitchDuration| field trial
201 // parameter. See |kParamMinutesMaxContextSwitchDuration| for details.
202 base::TimeDelta context_switch_duration_;
204 // If the device activity has never been fetched, the delay until the check
205 // will be a random duration between zero and
206 // |kParamHoursBetweenDeviceActivityChecks|. For all other
207 // fetches, the delay will be |kParamHoursBetweenDeviceActivityChecks|. See
208 // |kParamHoursBetweenDeviceActivityChecks| for details.
209 base::TimeDelta delay_until_next_device_activity_fetch_;
211 // Initialized from the |kParamDaysToVerifySingleUserProfile| field trial
212 // parameter. See |kParamDaysToVerifySingleUserProfile| for details.
213 base::TimeDelta single_account_duration_threshold_;
215 // Initialized from the |kParamMinutesBetweenBrowsingSessions| field trial
216 // parameter. See |kParamMinutesBetweenBrowsingSessions| for details.
217 base::TimeDelta inactivity_between_browsing_sessions_;
219 // Randomly initialized from the |kParamRPCThrottle| field trial parameter.
220 // See |kParamRPCThrottle| for details. If true, |device_activity_fetcher_|
221 // should never be initialized.
222 bool is_throttled_;
224 // Metric to help us track how long a browsing session is. This is set in
225 // MaybeBrowsingSessionStarted(), see that method for details.
226 // Useful for configuring the field trial to manage our server quota.
227 base::Time start_last_browsing_session_;
229 // Used to delay the check of device activity. See
230 // OnFetchDeviceActivitySuccess() or MaybeBrowsingSessionStarted(), as well as
231 // |delay_until_next_device_activity_fetch_|, for details.
232 base::OneShotTimer<CrossDevicePromo> device_activity_timer_;
234 DISALLOW_COPY_AND_ASSIGN(CrossDevicePromo);
237 #endif // CHROME_BROWSER_SIGNIN_CROSS_DEVICE_PROMO_H_