| // Copyright 2023 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/privacy_sandbox/tracking_protection_onboarding.h" |
| #include <optional> |
| #include "base/check.h" |
| #include "base/feature_list.h" |
| #include "base/functional/bind.h" |
| #include "base/metrics/histogram_functions.h" |
| #include "base/metrics/user_metrics.h" |
| #include "base/metrics/user_metrics_action.h" |
| #include "base/notreached.h" |
| #include "base/time/time.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/privacy_sandbox/privacy_sandbox_features.h" |
| #include "components/privacy_sandbox/tracking_protection_prefs.h" |
| #include "components/version_info/channel.h" |
| |
| namespace privacy_sandbox { |
| namespace { |
| |
| using ::privacy_sandbox::tracking_protection:: |
| TrackingProtectionOnboardingAckAction; |
| using ::privacy_sandbox::tracking_protection:: |
| TrackingProtectionOnboardingStatus; |
| |
| using ::privacy_sandbox::tracking_protection:: |
| TrackingProtectionSentimentSurveyGroup; |
| |
| using NoticeType = privacy_sandbox::TrackingProtectionOnboarding::NoticeType; |
| using SentimentSurveyGroup = |
| privacy_sandbox::TrackingProtectionOnboarding::SentimentSurveyGroup; |
| |
| constexpr base::TimeDelta kHatsImmediateStartTimeDelta = base::Minutes(2); |
| constexpr base::TimeDelta kHatsImmediateEndTimeDelta = base::Hours(1); |
| constexpr base::TimeDelta kHatsDelayedStartTimeDelta = base::Days(14); |
| constexpr base::TimeDelta kHatsDelayedEndTimeDelta = base::Days(15); |
| |
| TrackingProtectionOnboardingStatus GetInternalOnboardingStatus( |
| PrefService* pref_service) { |
| return static_cast<TrackingProtectionOnboardingStatus>( |
| pref_service->GetInteger(prefs::kTrackingProtectionOnboardingStatus)); |
| } |
| |
| TrackingProtectionOnboardingAckAction ToInternalAckAction( |
| TrackingProtectionOnboarding::NoticeAction action) { |
| switch (action) { |
| case TrackingProtectionOnboarding::NoticeAction::kOther: |
| return TrackingProtectionOnboardingAckAction::kOther; |
| case TrackingProtectionOnboarding::NoticeAction::kGotIt: |
| return TrackingProtectionOnboardingAckAction::kGotIt; |
| case TrackingProtectionOnboarding::NoticeAction::kSettings: |
| return TrackingProtectionOnboardingAckAction::kSettings; |
| case TrackingProtectionOnboarding::NoticeAction::kLearnMore: |
| return TrackingProtectionOnboardingAckAction::kLearnMore; |
| case TrackingProtectionOnboarding::NoticeAction::kClosed: |
| return TrackingProtectionOnboardingAckAction::kClosed; |
| } |
| } |
| |
| SentimentSurveyGroup GetSurveyGroup(PrefService* pref_service) { |
| auto internal_group = static_cast<TrackingProtectionSentimentSurveyGroup>( |
| pref_service->GetInteger(prefs::kTrackingProtectionSentimentSurveyGroup)); |
| switch (internal_group) { |
| case tracking_protection::TrackingProtectionSentimentSurveyGroup::kNotSet: |
| return SentimentSurveyGroup::kNotSet; |
| case tracking_protection::TrackingProtectionSentimentSurveyGroup:: |
| kControlImmediate: |
| return SentimentSurveyGroup::kControlImmediate; |
| case tracking_protection::TrackingProtectionSentimentSurveyGroup:: |
| kTreatmentImmediate: |
| return SentimentSurveyGroup::kTreatmentImmediate; |
| case tracking_protection::TrackingProtectionSentimentSurveyGroup:: |
| kControlDelayed: |
| return SentimentSurveyGroup::kControlDelayed; |
| case tracking_protection::TrackingProtectionSentimentSurveyGroup:: |
| kTreatmentDelayed: |
| return SentimentSurveyGroup::kTreatmentDelayed; |
| } |
| } |
| |
| void RecordActionMetrics(TrackingProtectionOnboarding::NoticeAction action) { |
| switch (action) { |
| case TrackingProtectionOnboarding::NoticeAction::kOther: |
| base::RecordAction( |
| base::UserMetricsAction("TrackingProtection.Notice.DismissedOther")); |
| break; |
| case TrackingProtectionOnboarding::NoticeAction::kGotIt: |
| base::RecordAction( |
| base::UserMetricsAction("TrackingProtection.Notice.GotItClicked")); |
| break; |
| case TrackingProtectionOnboarding::NoticeAction::kSettings: |
| base::RecordAction( |
| base::UserMetricsAction("TrackingProtection.Notice.SettingsClicked")); |
| break; |
| case TrackingProtectionOnboarding::NoticeAction::kLearnMore: |
| base::RecordAction(base::UserMetricsAction( |
| "TrackingProtection.Notice.LearnMoreClicked")); |
| break; |
| case TrackingProtectionOnboarding::NoticeAction::kClosed: |
| base::RecordAction( |
| base::UserMetricsAction("TrackingProtection.Notice.Closed")); |
| break; |
| } |
| } |
| |
| void CreateHistogramSentimentSurveyRegistration( |
| TrackingProtectionOnboarding::SentimentSurveyGroupMetrics group) { |
| base::UmaHistogramEnumeration( |
| "PrivacySandbox.TrackingProtection.SentimentSurvey.Registered", group); |
| } |
| |
| void EmitRegistrationHistogram(SentimentSurveyGroup group) { |
| switch (group) { |
| case TrackingProtectionOnboarding::SentimentSurveyGroup::kNotSet: |
| break; |
| case TrackingProtectionOnboarding::SentimentSurveyGroup::kControlImmediate: |
| CreateHistogramSentimentSurveyRegistration( |
| TrackingProtectionOnboarding::SentimentSurveyGroupMetrics:: |
| kControlImmediate); |
| break; |
| case TrackingProtectionOnboarding::SentimentSurveyGroup::kControlDelayed: |
| CreateHistogramSentimentSurveyRegistration( |
| TrackingProtectionOnboarding::SentimentSurveyGroupMetrics:: |
| kControlDelayed); |
| break; |
| case TrackingProtectionOnboarding::SentimentSurveyGroup:: |
| kTreatmentImmediate: |
| CreateHistogramSentimentSurveyRegistration( |
| TrackingProtectionOnboarding::SentimentSurveyGroupMetrics:: |
| kTreatmentImmediate); |
| break; |
| case TrackingProtectionOnboarding::SentimentSurveyGroup::kTreatmentDelayed: |
| CreateHistogramSentimentSurveyRegistration( |
| TrackingProtectionOnboarding::SentimentSurveyGroupMetrics:: |
| kTreatmentDelayed); |
| break; |
| } |
| } |
| |
| void CreateHistogramOnboardingStartupState( |
| TrackingProtectionOnboarding::OnboardingStartupState state) { |
| base::UmaHistogramEnumeration( |
| "PrivacySandbox.TrackingProtection.OnboardingStartup.State", state); |
| } |
| |
| void CreateTimingHistogramOnboardingStartup(const char* name, |
| base::TimeDelta sample) { |
| base::UmaHistogramCustomTimes(name, sample, base::Milliseconds(1), |
| base::Days(10), 100); |
| } |
| |
| void RecordOnboardedHistogramsOnStartup(PrefService* pref_service) { |
| if (!pref_service->GetBoolean(prefs::kTrackingProtectionOnboardingAcked)) { |
| CreateHistogramOnboardingStartupState( |
| TrackingProtectionOnboarding::OnboardingStartupState:: |
| kOnboardedWaitingToAck); |
| auto waiting_to_ack_since = |
| base::Time::Now() - |
| pref_service->GetTime(prefs::kTrackingProtectionOnboardedSince); |
| auto eligible_to_onboarded_duration = |
| pref_service->GetTime(prefs::kTrackingProtectionOnboardedSince) - |
| pref_service->GetTime(prefs::kTrackingProtectionEligibleSince); |
| CreateTimingHistogramOnboardingStartup( |
| "PrivacySandbox.TrackingProtection.OnboardingStartup.WaitingToAckSince", |
| waiting_to_ack_since); |
| CreateTimingHistogramOnboardingStartup( |
| "PrivacySandbox.TrackingProtection.OnboardingStartup." |
| "EligibleToOnboardedDuration", |
| eligible_to_onboarded_duration); |
| return; |
| } |
| auto eligible_to_onboarded_duration = |
| pref_service->GetTime(prefs::kTrackingProtectionOnboardedSince) - |
| pref_service->GetTime(prefs::kTrackingProtectionEligibleSince); |
| CreateTimingHistogramOnboardingStartup( |
| "PrivacySandbox.TrackingProtection.OnboardingStartup." |
| "EligibleToOnboardedDuration", |
| eligible_to_onboarded_duration); |
| auto action = static_cast<TrackingProtectionOnboardingAckAction>( |
| pref_service->GetInteger(prefs::kTrackingProtectionOnboardingAckAction)); |
| switch (action) { |
| case tracking_protection::TrackingProtectionOnboardingAckAction::kNotSet: |
| break; |
| case tracking_protection::TrackingProtectionOnboardingAckAction::kGotIt: |
| CreateHistogramOnboardingStartupState( |
| TrackingProtectionOnboarding::OnboardingStartupState::kAckedGotIt); |
| break; |
| case tracking_protection::TrackingProtectionOnboardingAckAction::kSettings: |
| CreateHistogramOnboardingStartupState( |
| TrackingProtectionOnboarding::OnboardingStartupState::kAckedSettings); |
| break; |
| case tracking_protection::TrackingProtectionOnboardingAckAction::kClosed: |
| CreateHistogramOnboardingStartupState( |
| TrackingProtectionOnboarding::OnboardingStartupState::kAckedClosed); |
| break; |
| case tracking_protection::TrackingProtectionOnboardingAckAction::kLearnMore: |
| CreateHistogramOnboardingStartupState( |
| TrackingProtectionOnboarding::OnboardingStartupState:: |
| kAckedLearnMore); |
| break; |
| case tracking_protection::TrackingProtectionOnboardingAckAction::kOther: |
| CreateHistogramOnboardingStartupState( |
| TrackingProtectionOnboarding::OnboardingStartupState::kAckedOther); |
| break; |
| } |
| } |
| |
| void RecordHistogramsOnStartup(PrefService* pref_service) { |
| auto status = GetInternalOnboardingStatus(pref_service); |
| switch (status) { |
| case TrackingProtectionOnboardingStatus::kIneligible: |
| CreateHistogramOnboardingStartupState( |
| TrackingProtectionOnboarding::OnboardingStartupState::kIneligible); |
| break; |
| case TrackingProtectionOnboardingStatus::kEligible: { |
| CreateHistogramOnboardingStartupState( |
| TrackingProtectionOnboarding::OnboardingStartupState:: |
| kEligibleWaitingToOnboard); |
| auto waiting_to_onboard_since = |
| base::Time::Now() - |
| pref_service->GetTime(prefs::kTrackingProtectionEligibleSince); |
| CreateTimingHistogramOnboardingStartup( |
| "PrivacySandbox.TrackingProtection.OnboardingStartup." |
| "WaitingToOnboardSince", |
| waiting_to_onboard_since); |
| break; |
| } |
| case TrackingProtectionOnboardingStatus::kOnboarded: |
| RecordOnboardedHistogramsOnStartup(pref_service); |
| break; |
| } |
| } |
| |
| bool IsRollbackEnabled() { |
| return base::FeatureList::IsEnabled( |
| privacy_sandbox::kTrackingProtectionOnboardingRollback); |
| } |
| |
| void OffboardingNoticeShown(PrefService* pref_service) { |
| if (pref_service->GetBoolean(prefs::kTrackingProtectionOffboarded)) { |
| return; |
| } |
| pref_service->SetBoolean(prefs::kTrackingProtectionOffboarded, true); |
| pref_service->SetTime(prefs::kTrackingProtectionOffboardedSince, |
| base::Time::Now()); |
| } |
| |
| void OffboardingNoticeActionTaken( |
| TrackingProtectionOnboarding::NoticeAction action, |
| PrefService* pref_service) { |
| pref_service->SetInteger(prefs::kTrackingProtectionOffboardingAckAction, |
| static_cast<int>(ToInternalAckAction(action))); |
| } |
| |
| void MaybeSetStartAndEndSurveyTime(PrefService* pref_service) { |
| if (pref_service->HasPrefPath( |
| prefs::kTrackingProtectionSentimentSurveyStartTime)) { |
| return; |
| } |
| |
| auto group = GetSurveyGroup(pref_service); |
| // Setting the start and end time when applicable. |
| // First, start by determining the anchor time. Control should be the first |
| // time this function runs. Treatment should be when the notice was acked. |
| // If the anchor time isn't year ready, return early. |
| base::Time anchor_time; |
| switch (group) { |
| case SentimentSurveyGroup::kNotSet: |
| return; |
| case SentimentSurveyGroup::kControlImmediate: |
| case SentimentSurveyGroup::kControlDelayed: |
| anchor_time = base::Time::Now(); |
| break; |
| case SentimentSurveyGroup::kTreatmentImmediate: |
| case SentimentSurveyGroup::kTreatmentDelayed: |
| if (!pref_service->HasPrefPath( |
| prefs::kTrackingProtectionOnboardingAckedSince)) { |
| return; |
| } |
| anchor_time = |
| pref_service->GetTime(prefs::kTrackingProtectionOnboardingAckedSince); |
| break; |
| } |
| |
| // Anchor time must be ready. Use it to set the start and end survey time on |
| // the profile. |
| switch (group) { |
| case SentimentSurveyGroup::kNotSet: |
| NOTREACHED_NORETURN(); |
| case SentimentSurveyGroup::kControlImmediate: |
| case SentimentSurveyGroup::kTreatmentImmediate: |
| pref_service->SetTime(prefs::kTrackingProtectionSentimentSurveyStartTime, |
| anchor_time + kHatsImmediateStartTimeDelta); |
| pref_service->SetTime(prefs::kTrackingProtectionSentimentSurveyEndTime, |
| anchor_time + kHatsImmediateEndTimeDelta); |
| EmitRegistrationHistogram(group); |
| return; |
| case SentimentSurveyGroup::kTreatmentDelayed: |
| case SentimentSurveyGroup::kControlDelayed: |
| pref_service->SetTime(prefs::kTrackingProtectionSentimentSurveyStartTime, |
| anchor_time + kHatsDelayedStartTimeDelta); |
| pref_service->SetTime(prefs::kTrackingProtectionSentimentSurveyEndTime, |
| anchor_time + kHatsDelayedEndTimeDelta); |
| EmitRegistrationHistogram(group); |
| return; |
| } |
| } |
| |
| } // namespace |
| |
| TrackingProtectionOnboarding::TrackingProtectionOnboarding( |
| PrefService* pref_service, |
| version_info::Channel channel) |
| : pref_service_(pref_service), channel_(channel) { |
| CHECK(pref_service_); |
| |
| pref_change_registrar_.Init(pref_service_); |
| pref_change_registrar_.Add( |
| prefs::kTrackingProtectionOnboardingStatus, |
| base::BindRepeating( |
| &TrackingProtectionOnboarding::OnOnboardingPrefChanged, |
| base::Unretained(this))); |
| pref_change_registrar_.Add( |
| prefs::kTrackingProtectionOnboardingAcked, |
| base::BindRepeating( |
| &TrackingProtectionOnboarding::OnOnboardingAckedChanged, |
| base::Unretained(this))); |
| pref_change_registrar_.Add( |
| prefs::kTrackingProtectionOffboarded, |
| base::BindRepeating( |
| &TrackingProtectionOnboarding::OnOffboardingPrefChanged, |
| base::Unretained(this))); |
| |
| RecordHistogramsOnStartup(pref_service_); |
| } |
| |
| TrackingProtectionOnboarding::~TrackingProtectionOnboarding() = default; |
| |
| void TrackingProtectionOnboarding::OnOnboardingPrefChanged() const { |
| // We notify observers of all changes to the onboarding pref. |
| auto onboarding_status = GetOnboardingStatus(); |
| for (auto& observer : observers_) { |
| observer.OnTrackingProtectionOnboardingUpdated(onboarding_status); |
| } |
| |
| switch (GetInternalOnboardingStatus(pref_service_)) { |
| case tracking_protection::TrackingProtectionOnboardingStatus::kIneligible: |
| case tracking_protection::TrackingProtectionOnboardingStatus::kEligible: |
| for (auto& observer : observers_) { |
| observer.OnShouldShowNoticeUpdated(); |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| |
| void TrackingProtectionOnboarding::OnOnboardingAckedChanged() const { |
| // Maybe set the Hats start and end time now that the profile is onboarded. |
| MaybeSetStartAndEndSurveyTime(pref_service_); |
| for (auto& observer : observers_) { |
| observer.OnShouldShowNoticeUpdated(); |
| } |
| } |
| |
| void TrackingProtectionOnboarding::OnOffboardingPrefChanged() const { |
| for (auto& observer : observers_) { |
| observer.OnTrackingProtectionOnboardingUpdated(GetOnboardingStatus()); |
| } |
| } |
| |
| void TrackingProtectionOnboarding::MaybeMarkEligible() { |
| auto status = GetInternalOnboardingStatus(pref_service_); |
| if (status != TrackingProtectionOnboardingStatus::kIneligible) { |
| base::UmaHistogramBoolean( |
| "PrivacySandbox.TrackingProtection.Onboarding.MaybeMarkEligible", |
| false); |
| return; |
| } |
| pref_service_->SetTime(prefs::kTrackingProtectionEligibleSince, |
| base::Time::Now()); |
| pref_service_->SetInteger( |
| prefs::kTrackingProtectionOnboardingStatus, |
| static_cast<int>( |
| TrackingProtectionOnboarding::OnboardingStatus::kEligible)); |
| base::UmaHistogramBoolean( |
| "PrivacySandbox.TrackingProtection.Onboarding.MaybeMarkEligible", true); |
| } |
| |
| void TrackingProtectionOnboarding::MaybeMarkIneligible() { |
| auto status = GetInternalOnboardingStatus(pref_service_); |
| if (status != TrackingProtectionOnboardingStatus::kEligible) { |
| base::UmaHistogramBoolean( |
| "PrivacySandbox.TrackingProtection.Onboarding.MaybeMarkIneligible", |
| false); |
| return; |
| } |
| pref_service_->ClearPref(prefs::kTrackingProtectionEligibleSince); |
| pref_service_->SetInteger( |
| prefs::kTrackingProtectionOnboardingStatus, |
| static_cast<int>( |
| TrackingProtectionOnboarding::OnboardingStatus::kIneligible)); |
| base::UmaHistogramBoolean( |
| "PrivacySandbox.TrackingProtection.Onboarding.MaybeMarkIneligible", true); |
| } |
| |
| bool TrackingProtectionOnboarding::RequiresSentimentSurveyGroup() { |
| // If the pref is in the default value, it means we haven't added this profile |
| // to any of the groups yet. |
| return !pref_service_->HasPrefPath( |
| prefs::kTrackingProtectionSentimentSurveyGroup); |
| } |
| |
| void TrackingProtectionOnboarding::RegisterSentimentSurveyGroup( |
| SentimentSurveyGroup group) { |
| // Setting the group first. |
| tracking_protection::TrackingProtectionSentimentSurveyGroup internal_group; |
| switch (group) { |
| case SentimentSurveyGroup::kNotSet: |
| return; |
| case SentimentSurveyGroup::kControlImmediate: |
| internal_group = |
| TrackingProtectionSentimentSurveyGroup::kControlImmediate; |
| break; |
| case SentimentSurveyGroup::kTreatmentImmediate: |
| internal_group = |
| TrackingProtectionSentimentSurveyGroup::kTreatmentImmediate; |
| break; |
| case SentimentSurveyGroup::kControlDelayed: |
| internal_group = TrackingProtectionSentimentSurveyGroup::kControlDelayed; |
| break; |
| case SentimentSurveyGroup::kTreatmentDelayed: |
| internal_group = |
| TrackingProtectionSentimentSurveyGroup::kTreatmentDelayed; |
| break; |
| } |
| |
| pref_service_->SetInteger(prefs::kTrackingProtectionSentimentSurveyGroup, |
| static_cast<int>(internal_group)); |
| |
| MaybeSetStartAndEndSurveyTime(pref_service_); |
| } |
| |
| TrackingProtectionOnboarding::SentimentSurveyGroup |
| TrackingProtectionOnboarding::GetEligibleSurveyGroup() { |
| SentimentSurveyGroup group = GetSurveyGroup(pref_service_); |
| |
| if (group == SentimentSurveyGroup::kNotSet) { |
| return group; |
| } |
| |
| // If Start time isn't set, it means the profile isn't yet eligible. This is |
| // because all surveys MUST have a start time. |
| if (!pref_service_->HasPrefPath( |
| prefs::kTrackingProtectionSentimentSurveyStartTime)) { |
| return SentimentSurveyGroup::kNotSet; |
| } |
| |
| if (base::Time::Now() < |
| pref_service_->GetTime( |
| prefs::kTrackingProtectionSentimentSurveyStartTime)) { |
| return SentimentSurveyGroup::kNotSet; |
| } |
| |
| // No additional filtering if the end time isn't set. This is to support open |
| // ended surveys. |
| if (!pref_service_->HasPrefPath( |
| prefs::kTrackingProtectionSentimentSurveyEndTime)) { |
| return group; |
| } |
| |
| // At this point, an end date is set, so we need to use it for filtering. |
| if (base::Time::Now() > |
| pref_service_->GetTime( |
| prefs::kTrackingProtectionSentimentSurveyEndTime)) { |
| return SentimentSurveyGroup::kNotSet; |
| } |
| |
| return group; |
| } |
| |
| void TrackingProtectionOnboarding::MaybeResetOnboardingPrefs() { |
| // Clearing the prefs is only allowed in Beta, Canary and Dev for testing. |
| switch (channel_) { |
| case version_info::Channel::BETA: |
| case version_info::Channel::CANARY: |
| case version_info::Channel::DEV: |
| break; |
| default: |
| return; |
| } |
| |
| // Clear all Onboarding Prefs. Excluding Ack prefs. |
| pref_service_->ClearPref(prefs::kTrackingProtectionOnboardingStatus); |
| pref_service_->ClearPref(prefs::kTrackingProtectionEligibleSince); |
| pref_service_->ClearPref(prefs::kTrackingProtectionOnboardedSince); |
| pref_service_->ClearPref(prefs::kTrackingProtectionNoticeLastShown); |
| } |
| |
| void TrackingProtectionOnboarding::OnboardingNoticeShown() { |
| base::RecordAction( |
| base::UserMetricsAction("TrackingProtection.Notice.Shown")); |
| |
| pref_service_->SetTime(prefs::kTrackingProtectionNoticeLastShown, |
| base::Time::Now()); |
| |
| auto status = GetInternalOnboardingStatus(pref_service_); |
| if (status != TrackingProtectionOnboardingStatus::kEligible) { |
| base::UmaHistogramBoolean( |
| "PrivacySandbox.TrackingProtection.Onboarding.DidNoticeShownOnboard", |
| false); |
| return; |
| } |
| pref_service_->SetTime(prefs::kTrackingProtectionOnboardedSince, |
| base::Time::Now()); |
| pref_service_->SetInteger( |
| prefs::kTrackingProtectionOnboardingStatus, |
| static_cast<int>( |
| TrackingProtectionOnboarding::OnboardingStatus::kOnboarded)); |
| auto eligible_to_onboarded_duration = |
| pref_service_->GetTime(prefs::kTrackingProtectionOnboardedSince) - |
| pref_service_->GetTime(prefs::kTrackingProtectionEligibleSince); |
| CreateTimingHistogramOnboardingStartup( |
| "PrivacySandbox.TrackingProtection.Onboarding." |
| "EligibleToOnboardedDuration", |
| eligible_to_onboarded_duration); |
| base::UmaHistogramBoolean( |
| "PrivacySandbox.TrackingProtection.Onboarding.DidNoticeShownOnboard", |
| true); |
| } |
| |
| void TrackingProtectionOnboarding::NoticeShown(NoticeType notice_type) { |
| switch (notice_type) { |
| case NoticeType::kNone: |
| return; |
| case NoticeType::kOnboarding: |
| OnboardingNoticeShown(); |
| return; |
| case NoticeType::kOffboarding: |
| OffboardingNoticeShown(pref_service_); |
| return; |
| } |
| } |
| |
| void TrackingProtectionOnboarding::OnboardingNoticeActionTaken( |
| NoticeAction action) { |
| RecordActionMetrics(action); |
| |
| if (pref_service_->GetBoolean(prefs::kTrackingProtectionOnboardingAcked)) { |
| base::UmaHistogramBoolean( |
| "PrivacySandbox.TrackingProtection.Onboarding." |
| "DidNoticeActionAckowledge", |
| false); |
| return; |
| } |
| |
| pref_service_->SetTime(prefs::kTrackingProtectionOnboardingAckedSince, |
| base::Time::Now()); |
| pref_service_->SetInteger(prefs::kTrackingProtectionOnboardingAckAction, |
| static_cast<int>(ToInternalAckAction(action))); |
| pref_service_->SetBoolean(prefs::kTrackingProtectionOnboardingAcked, true); |
| |
| auto onboarding_to_acked_duration = |
| base::Time::Now() - |
| pref_service_->GetTime(prefs::kTrackingProtectionOnboardedSince); |
| auto last_shown_to_acked_duration = |
| base::Time::Now() - |
| pref_service_->GetTime(prefs::kTrackingProtectionNoticeLastShown); |
| CreateTimingHistogramOnboardingStartup( |
| "PrivacySandbox.TrackingProtection.Onboarding.OnboardedToAckedDuration", |
| onboarding_to_acked_duration); |
| CreateTimingHistogramOnboardingStartup( |
| "PrivacySandbox.TrackingProtection.Onboarding.LastShownToAckedDuration", |
| last_shown_to_acked_duration); |
| base::UmaHistogramBoolean( |
| "PrivacySandbox.TrackingProtection.Onboarding." |
| "DidNoticeActionAckowledge", |
| true); |
| } |
| |
| void TrackingProtectionOnboarding::NoticeActionTaken(NoticeType notice_type, |
| NoticeAction action) { |
| switch (notice_type) { |
| case NoticeType::kNone: |
| return; |
| case NoticeType::kOnboarding: |
| OnboardingNoticeActionTaken(action); |
| return; |
| case NoticeType::kOffboarding: |
| OffboardingNoticeActionTaken(action, pref_service_); |
| return; |
| } |
| } |
| |
| bool TrackingProtectionOnboarding::ShouldShowOnboardingNotice() { |
| return GetRequiredNotice() == NoticeType::kOnboarding; |
| } |
| |
| NoticeType TrackingProtectionOnboarding::GetRequiredNotice() { |
| auto onboarding_status = GetInternalOnboardingStatus(pref_service_); |
| switch (onboarding_status) { |
| case TrackingProtectionOnboardingStatus::kIneligible: |
| return NoticeType::kNone; |
| case TrackingProtectionOnboardingStatus::kEligible: |
| // We haven't showed the user any notice yet. only shown them the |
| // onboarding notice if we're not planning on offboarding them. |
| return IsRollbackEnabled() ? NoticeType::kNone : NoticeType::kOnboarding; |
| case TrackingProtectionOnboardingStatus::kOnboarded: |
| // We've already showed the user the onboarding notice. We |
| // offboard them if applicable. Otherwise, we keep showing the |
| // Onboarding Notice until they Ack. |
| if (IsRollbackEnabled()) { |
| return pref_service_->GetBoolean(prefs::kTrackingProtectionOffboarded) |
| ? NoticeType::kNone |
| : NoticeType::kOffboarding; |
| } |
| return pref_service_->GetBoolean( |
| prefs::kTrackingProtectionOnboardingAcked) |
| ? NoticeType::kNone |
| : NoticeType::kOnboarding; |
| } |
| } |
| |
| bool TrackingProtectionOnboarding::IsOffboarded() const { |
| return GetOnboardingStatus() == OnboardingStatus::kOffboarded; |
| } |
| |
| std::optional<base::TimeDelta> |
| TrackingProtectionOnboarding::OnboardedToAcknowledged() { |
| if (!pref_service_->HasPrefPath( |
| prefs::kTrackingProtectionOnboardingAckedSince)) { |
| return std::nullopt; |
| } |
| if (!pref_service_->HasPrefPath(prefs::kTrackingProtectionOnboardedSince)) { |
| return std::nullopt; |
| } |
| return pref_service_->GetTime( |
| prefs::kTrackingProtectionOnboardingAckedSince) - |
| pref_service_->GetTime(prefs::kTrackingProtectionOnboardedSince); |
| } |
| |
| TrackingProtectionOnboarding::OnboardingStatus |
| TrackingProtectionOnboarding::GetOnboardingStatus() const { |
| if (IsRollbackEnabled() && |
| pref_service_->GetBoolean(prefs::kTrackingProtectionOffboarded)) { |
| return OnboardingStatus::kOffboarded; |
| } |
| auto onboarding_status = GetInternalOnboardingStatus(pref_service_); |
| switch (onboarding_status) { |
| case TrackingProtectionOnboardingStatus::kIneligible: |
| return OnboardingStatus::kIneligible; |
| case TrackingProtectionOnboardingStatus::kEligible: |
| return OnboardingStatus::kEligible; |
| case TrackingProtectionOnboardingStatus::kOnboarded: |
| return OnboardingStatus::kOnboarded; |
| } |
| } |
| |
| void TrackingProtectionOnboarding::AddObserver(Observer* observer) { |
| observers_.AddObserver(observer); |
| } |
| |
| void TrackingProtectionOnboarding::RemoveObserver(Observer* observer) { |
| observers_.RemoveObserver(observer); |
| } |
| |
| } // namespace privacy_sandbox |