1 // Copyright 2017 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.
4
5 #include "chrome/browser/chromeos/login/screens/sync_consent_screen.h"
6
7 #include <string>
8
9 #include "base/bind.h"
10 #include "base/check.h"
11 #include "base/metrics/histogram_functions.h"
12 #include "chrome/browser/chromeos/profiles/profile_helper.h"
13 #include "chrome/browser/consent_auditor/consent_auditor_factory.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/signin/identity_manager_factory.h"
16 #include "chrome/browser/sync/profile_sync_service_factory.h"
17 #include "chrome/browser/ui/chrome_pages.h"
18 #include "chrome/browser/unified_consent/unified_consent_service_factory.h"
19 #include "chrome/common/pref_names.h"
20 #include "chrome/common/webui_url_constants.h"
21 #include "chromeos/constants/chromeos_features.h"
22 #include "chromeos/constants/chromeos_pref_names.h"
23 #include "components/consent_auditor/consent_auditor.h"
24 #include "components/prefs/pref_service.h"
25 #include "components/signin/public/identity_manager/consent_level.h"
26 #include "components/signin/public/identity_manager/identity_manager.h"
27 #include "components/signin/public/identity_manager/primary_account_mutator.h"
28 #include "components/sync/base/pref_names.h"
29 #include "components/sync/base/user_selectable_type.h"
30 #include "components/sync/driver/sync_service.h"
31 #include "components/sync/driver/sync_user_settings.h"
32 #include "components/unified_consent/unified_consent_service.h"
33 #include "components/user_manager/user_manager.h"
34
35 namespace chromeos {
36 namespace {
37
38 // Delay showing chrome sync settings by this amount of time to make them
39 // show on top of the restored tabs and windows.
40 constexpr base::TimeDelta kSyncConsentSettingsShowDelay =
41 base::TimeDelta::FromSeconds(3);
42
GetSyncService(Profile * profile)43 syncer::SyncService* GetSyncService(Profile* profile) {
44 if (ProfileSyncServiceFactory::HasSyncService(profile))
45 return ProfileSyncServiceFactory::GetForProfile(profile);
46 return nullptr;
47 }
48
RecordUmaReviewFollowingSetup(bool value)49 void RecordUmaReviewFollowingSetup(bool value) {
50 base::UmaHistogramBoolean("OOBE.SyncConsentScreen.ReviewFollowingSetup",
51 value);
52 }
53
54 } // namespace
55
56 // static
57 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
58 bool g_is_branded_build = true;
59 #else
60 bool g_is_branded_build = false;
61 #endif
62
63 // static
GetResultString(Result result)64 std::string SyncConsentScreen::GetResultString(Result result) {
65 switch (result) {
66 case Result::NEXT:
67 return "Next";
68 case Result::NOT_APPLICABLE:
69 return BaseScreen::kNotApplicable;
70 }
71 }
72
73 // static
MaybeLaunchSyncConsentSettings(Profile * profile)74 void SyncConsentScreen::MaybeLaunchSyncConsentSettings(Profile* profile) {
75 if (profile->GetPrefs()->GetBoolean(
76 ::prefs::kShowSyncSettingsOnSessionStart)) {
77 // TODO (alemate): In a very special case when chrome is exiting at the very
78 // moment we show Settings, it might crash here because profile could be
79 // already destroyed. This needs to be fixed.
80 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
81 FROM_HERE,
82 base::BindOnce(
83 [](Profile* profile) {
84 profile->GetPrefs()->ClearPref(
85 ::prefs::kShowSyncSettingsOnSessionStart);
86 chrome::ShowSettingsSubPageForProfile(profile,
87 chrome::kSyncSetupSubPage);
88 },
89 base::Unretained(profile)),
90 kSyncConsentSettingsShowDelay);
91 }
92 }
93
SyncConsentScreen(SyncConsentScreenView * view,const ScreenExitCallback & exit_callback)94 SyncConsentScreen::SyncConsentScreen(SyncConsentScreenView* view,
95 const ScreenExitCallback& exit_callback)
96 : BaseScreen(SyncConsentScreenView::kScreenId, OobeScreenPriority::DEFAULT),
97 view_(view),
98 exit_callback_(exit_callback) {
99 DCHECK(view_);
100 view_->Bind(this);
101 }
102
~SyncConsentScreen()103 SyncConsentScreen::~SyncConsentScreen() {
104 view_->Bind(NULL);
105 }
106
Init()107 void SyncConsentScreen::Init() {
108 if (is_initialized_)
109 return;
110 is_initialized_ = true;
111 user_ = user_manager::UserManager::Get()->GetPrimaryUser();
112 profile_ = ProfileHelper::Get()->GetProfileByUser(user_);
113 UpdateScreen();
114 }
115
Finish(Result result)116 void SyncConsentScreen::Finish(Result result) {
117 DCHECK(profile_);
118 // Always set completed, even if the dialog was skipped (e.g. by policy).
119 profile_->GetPrefs()->SetBoolean(chromeos::prefs::kSyncOobeCompleted, true);
120 // Record whether the dialog was shown, skipped, etc.
121 base::UmaHistogramEnumeration("OOBE.SyncConsentScreen.Behavior", behavior_);
122 // Record the final state of the sync service.
123 syncer::SyncService* service = GetSyncService(profile_);
124 bool sync_enabled = service && service->CanSyncFeatureStart() &&
125 service->GetUserSettings()->IsSyncEverythingEnabled();
126 base::UmaHistogramBoolean("OOBE.SyncConsentScreen.SyncEnabled", sync_enabled);
127 exit_callback_.Run(result);
128 }
129
MaybeSkip(WizardContext * context)130 bool SyncConsentScreen::MaybeSkip(WizardContext* context) {
131 Init();
132
133 switch (behavior_) {
134 case SyncScreenBehavior::kUnknown:
135 case SyncScreenBehavior::kShow:
136 return false;
137 case SyncScreenBehavior::kSkipNonGaiaAccount:
138 case SyncScreenBehavior::kSkipPublicAccount:
139 case SyncScreenBehavior::kSkipFeaturePolicy:
140 case SyncScreenBehavior::kSkipAndEnableNonBrandedBuild:
141 case SyncScreenBehavior::kSkipAndEnableEmphemeralUser:
142 case SyncScreenBehavior::kSkipAndEnableScreenPolicy:
143 MaybeEnableSyncForSkip();
144 Finish(Result::NOT_APPLICABLE);
145 return true;
146 }
147 }
148
ShowImpl()149 void SyncConsentScreen::ShowImpl() {
150 Init();
151
152 if (behavior_ != SyncScreenBehavior::kShow) {
153 // Wait for updates and set the loading throbber to be visible.
154 view_->SetThrobberVisible(true /*visible*/);
155 syncer::SyncService* service = GetSyncService(profile_);
156 if (service)
157 sync_service_observer_.Add(service);
158 }
159 // Show the entire screen.
160 // If SyncScreenBehavior is show, this should show the sync consent screen.
161 // If SyncScreenBehavior is unknown, this should show the loading throbber.
162 view_->Show();
163 }
164
HideImpl()165 void SyncConsentScreen::HideImpl() {
166 sync_service_observer_.RemoveAll();
167 view_->Hide();
168 }
169
OnStateChanged(syncer::SyncService * sync)170 void SyncConsentScreen::OnStateChanged(syncer::SyncService* sync) {
171 UpdateScreen();
172 }
173
OnContinueAndReview(const std::vector<int> & consent_description,const int consent_confirmation)174 void SyncConsentScreen::OnContinueAndReview(
175 const std::vector<int>& consent_description,
176 const int consent_confirmation) {
177 if (is_hidden())
178 return;
179 RecordUmaReviewFollowingSetup(true);
180 RecordConsent(CONSENT_GIVEN, consent_description, consent_confirmation);
181 profile_->GetPrefs()->SetBoolean(::prefs::kShowSyncSettingsOnSessionStart,
182 true);
183 Finish(Result::NEXT);
184 }
185
OnContinueWithDefaults(const std::vector<int> & consent_description,const int consent_confirmation)186 void SyncConsentScreen::OnContinueWithDefaults(
187 const std::vector<int>& consent_description,
188 const int consent_confirmation) {
189 if (is_hidden())
190 return;
191 RecordUmaReviewFollowingSetup(false);
192 RecordConsent(CONSENT_GIVEN, consent_description, consent_confirmation);
193 Finish(Result::NEXT);
194 }
195
OnContinue(const std::vector<int> & consent_description,int consent_confirmation,SyncConsentScreenHandler::UserChoice choice)196 void SyncConsentScreen::OnContinue(
197 const std::vector<int>& consent_description,
198 int consent_confirmation,
199 SyncConsentScreenHandler::UserChoice choice) {
200 DCHECK(chromeos::features::IsSplitSettingsSyncEnabled());
201 if (is_hidden())
202 return;
203 base::UmaHistogramEnumeration("OOBE.SyncConsentScreen.UserChoice", choice);
204 // Record that the user saw the consent text, regardless of which features
205 // they chose to enable.
206 RecordConsent(CONSENT_GIVEN, consent_description, consent_confirmation);
207 bool enable_sync = choice == SyncConsentScreenHandler::UserChoice::kAccepted;
208 UpdateSyncSettings(enable_sync);
209 Finish(Result::NEXT);
210 }
211
UpdateSyncSettings(bool enable_sync)212 void SyncConsentScreen::UpdateSyncSettings(bool enable_sync) {
213 DCHECK(chromeos::features::IsSplitSettingsSyncEnabled());
214 // For historical reasons, Chrome OS always has a "sync-consented" primary
215 // account in IdentityManager and always has browser sync "enabled". If the
216 // user disables the browser sync toggle we disable all browser data types,
217 // as if the user had opened browser sync settings and turned off all the
218 // toggles.
219 // TODO(crbug.com/1046746, crbug.com/1050677): Once all Chrome OS code is
220 // converted to the "consent aware" IdentityManager API, and the browser sync
221 // settings WebUI is converted to allow browser sync to be turned on/off, then
222 // this workaround can be removed.
223 syncer::SyncService* sync_service = GetSyncService(profile_);
224 if (sync_service) {
225 syncer::SyncUserSettings* sync_settings = sync_service->GetUserSettings();
226 sync_settings->SetOsSyncFeatureEnabled(enable_sync);
227 if (!enable_sync) {
228 syncer::UserSelectableTypeSet empty_set;
229 sync_settings->SetSelectedTypes(/*sync_everything=*/false, empty_set);
230 }
231 sync_settings->SetSyncRequested(true);
232 sync_settings->SetFirstSetupComplete(
233 syncer::SyncFirstSetupCompleteSource::BASIC_FLOW);
234 }
235 // Set a "sync-consented" primary account. See comment above.
236 auto* identity_manager = IdentityManagerFactory::GetForProfile(profile_);
237 CoreAccountId account_id =
238 identity_manager->GetPrimaryAccountId(signin::ConsentLevel::kNotRequired);
239 DCHECK(!account_id.empty());
240 identity_manager->GetPrimaryAccountMutator()->SetPrimaryAccount(account_id);
241
242 // Only enable URL-keyed metrics if the user turned on browser sync.
243 if (enable_sync) {
244 unified_consent::UnifiedConsentService* consent_service =
245 UnifiedConsentServiceFactory::GetForProfile(profile_);
246 if (consent_service)
247 consent_service->SetUrlKeyedAnonymizedDataCollectionEnabled(true);
248 }
249 }
250
MaybeEnableSyncForSkip()251 void SyncConsentScreen::MaybeEnableSyncForSkip() {
252 // Prior to SplitSettingsSync, sync is autostarted during ProfileSyncService
253 // creation, so sync is already in the right state.
254 if (!chromeos::features::IsSplitSettingsSyncEnabled())
255 return;
256
257 switch (behavior_) {
258 case SyncScreenBehavior::kUnknown:
259 case SyncScreenBehavior::kShow:
260 NOTREACHED();
261 return;
262 case SyncScreenBehavior::kSkipNonGaiaAccount:
263 case SyncScreenBehavior::kSkipPublicAccount:
264 case SyncScreenBehavior::kSkipFeaturePolicy:
265 // Nothing to do.
266 return;
267 case SyncScreenBehavior::kSkipAndEnableNonBrandedBuild:
268 case SyncScreenBehavior::kSkipAndEnableEmphemeralUser:
269 case SyncScreenBehavior::kSkipAndEnableScreenPolicy:
270 UpdateSyncSettings(/*enable_sync=*/true);
271 return;
272 }
273 }
274
275 // static
276 std::unique_ptr<base::AutoReset<bool>>
ForceBrandedBuildForTesting(bool value)277 SyncConsentScreen::ForceBrandedBuildForTesting(bool value) {
278 return std::make_unique<base::AutoReset<bool>>(&g_is_branded_build, value);
279 }
280
281 // static
IsBrandedBuildForTesting()282 bool SyncConsentScreen::IsBrandedBuildForTesting() {
283 return g_is_branded_build;
284 }
285
SetDelegateForTesting(SyncConsentScreen::SyncConsentScreenTestDelegate * delegate)286 void SyncConsentScreen::SetDelegateForTesting(
287 SyncConsentScreen::SyncConsentScreenTestDelegate* delegate) {
288 test_delegate_ = delegate;
289 }
290
291 SyncConsentScreen::SyncConsentScreenTestDelegate*
GetDelegateForTesting() const292 SyncConsentScreen::GetDelegateForTesting() const {
293 return test_delegate_;
294 }
295
GetSyncScreenBehavior() const296 SyncConsentScreen::SyncScreenBehavior SyncConsentScreen::GetSyncScreenBehavior()
297 const {
298 // Skip for users without Gaia account (e.g. Active Directory).
299 if (!user_->HasGaiaAccount())
300 return SyncScreenBehavior::kSkipNonGaiaAccount;
301
302 // Skip for public user.
303 if (user_->GetType() == user_manager::USER_TYPE_PUBLIC_ACCOUNT)
304 return SyncScreenBehavior::kSkipPublicAccount;
305
306 // Skip for non-branded (e.g. developer) builds. Check this after the account
307 // type checks so we don't try to enable sync in browser_tests for those
308 // account types.
309 if (!g_is_branded_build)
310 return SyncScreenBehavior::kSkipAndEnableNonBrandedBuild;
311
312 const user_manager::UserManager* user_manager =
313 user_manager::UserManager::Get();
314 // Skip for non-regular ephemeral users.
315 if (user_manager->IsUserNonCryptohomeDataEphemeral(user_->GetAccountId()) &&
316 (user_->GetType() != user_manager::USER_TYPE_REGULAR)) {
317 return SyncScreenBehavior::kSkipAndEnableEmphemeralUser;
318 }
319
320 // Skip if the sync consent screen is disabled by policy, for example, in
321 // education scenarios. https://crbug.com/841156
322 if (!profile_->GetPrefs()->GetBoolean(::prefs::kEnableSyncConsent))
323 return SyncScreenBehavior::kSkipAndEnableScreenPolicy;
324
325 // Skip if sync-the-feature is disabled by policy.
326 if (IsProfileSyncDisabledByPolicy())
327 return SyncScreenBehavior::kSkipFeaturePolicy;
328
329 if (IsProfileSyncEngineInitialized())
330 return SyncScreenBehavior::kShow;
331
332 return SyncScreenBehavior::kUnknown;
333 }
334
UpdateScreen()335 void SyncConsentScreen::UpdateScreen() {
336 const SyncScreenBehavior new_behavior = GetSyncScreenBehavior();
337 if (new_behavior == SyncScreenBehavior::kUnknown)
338 return;
339
340 const SyncScreenBehavior old_behavior = behavior_;
341 behavior_ = new_behavior;
342
343 if (is_hidden() || behavior_ == old_behavior)
344 return;
345
346 if (behavior_ == SyncScreenBehavior::kShow) {
347 view_->SetThrobberVisible(false /*visible*/);
348 GetSyncService(profile_)->RemoveObserver(this);
349 } else {
350 MaybeEnableSyncForSkip();
351 Finish(Result::NEXT);
352 }
353 }
354
RecordConsent(ConsentGiven consent_given,const std::vector<int> & consent_description,int consent_confirmation)355 void SyncConsentScreen::RecordConsent(
356 ConsentGiven consent_given,
357 const std::vector<int>& consent_description,
358 int consent_confirmation) {
359 consent_auditor::ConsentAuditor* consent_auditor =
360 ConsentAuditorFactory::GetForProfile(profile_);
361 // The user might not consent to browser sync, so use the "unconsented" ID.
362 const CoreAccountId& google_account_id =
363 IdentityManagerFactory::GetForProfile(profile_)->GetPrimaryAccountId(
364 signin::ConsentLevel::kNotRequired);
365 // TODO(alemate): Support unified_consent_enabled
366 sync_pb::UserConsentTypes::SyncConsent sync_consent;
367 sync_consent.set_confirmation_grd_id(consent_confirmation);
368 for (int id : consent_description) {
369 sync_consent.add_description_grd_ids(id);
370 }
371 sync_consent.set_status(consent_given == CONSENT_GIVEN
372 ? sync_pb::UserConsentTypes::GIVEN
373 : sync_pb::UserConsentTypes::NOT_GIVEN);
374 consent_auditor->RecordSyncConsent(google_account_id, sync_consent);
375
376 if (test_delegate_) {
377 test_delegate_->OnConsentRecordedIds(consent_given, consent_description,
378 consent_confirmation);
379 }
380 }
381
IsProfileSyncDisabledByPolicy() const382 bool SyncConsentScreen::IsProfileSyncDisabledByPolicy() const {
383 if (test_sync_disabled_by_policy_.has_value())
384 return test_sync_disabled_by_policy_.value();
385 const syncer::SyncService* sync_service = GetSyncService(profile_);
386 return sync_service->HasDisableReason(
387 syncer::SyncService::DISABLE_REASON_ENTERPRISE_POLICY);
388 }
389
IsProfileSyncEngineInitialized() const390 bool SyncConsentScreen::IsProfileSyncEngineInitialized() const {
391 if (test_sync_engine_initialized_.has_value())
392 return test_sync_engine_initialized_.value();
393 const syncer::SyncService* sync_service = GetSyncService(profile_);
394 return sync_service->IsEngineInitialized();
395 }
396
SetProfileSyncDisabledByPolicyForTesting(bool value)397 void SyncConsentScreen::SetProfileSyncDisabledByPolicyForTesting(bool value) {
398 test_sync_disabled_by_policy_ = value;
399 }
SetProfileSyncEngineInitializedForTesting(bool value)400 void SyncConsentScreen::SetProfileSyncEngineInitializedForTesting(bool value) {
401 test_sync_engine_initialized_ = value;
402 }
403
404 } // namespace chromeos
405