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