xref: /aosp_15_r20/external/cronet/components/metrics/demographics/demographic_metrics_provider.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2019 The Chromium Authors
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 "components/metrics/demographics/demographic_metrics_provider.h"
6 
7 #include <optional>
8 
9 #include "base/feature_list.h"
10 #include "base/metrics/histogram_functions.h"
11 #include "base/notreached.h"
12 #include "build/chromeos_buildflags.h"
13 #include "components/sync/base/features.h"
14 #include "components/sync/service/sync_service.h"
15 #include "components/sync/service/sync_service_utils.h"
16 #include "third_party/metrics_proto/ukm/report.pb.h"
17 
18 namespace metrics {
19 
20 namespace {
21 
IsValidUploadState(syncer::UploadState upload_state)22 bool IsValidUploadState(syncer::UploadState upload_state) {
23   switch (upload_state) {
24     case syncer::UploadState::NOT_ACTIVE:
25       return false;
26     case syncer::UploadState::INITIALIZING:
27       // Note that INITIALIZING is considered good enough, because sync is known
28       // to be enabled, and transient errors don't really matter here.
29     case syncer::UploadState::ACTIVE:
30       return true;
31   }
32   NOTREACHED_NORETURN();
33 }
34 
CanUploadDemographicsToGoogle(syncer::SyncService * sync_service)35 bool CanUploadDemographicsToGoogle(syncer::SyncService* sync_service) {
36   CHECK(sync_service);
37 
38   // PRIORITY_PREFERENCES is the sync datatype used to propagate demographics
39   // information to the client. In its absence, demographics info is unavailable
40   // thus cannot be uploaded.
41   if (!IsValidUploadState(syncer::GetUploadToGoogleState(
42           sync_service, syncer::PRIORITY_PREFERENCES))) {
43     return false;
44   }
45 
46   // Even if GetUploadToGoogleState() reports to be active, the user may be in
47   // transport mode or full-sync (aka sync-the-feature enabled) mode.
48   // If `kReplaceSyncPromosWithSignInPromos` is enabled, then
49   // PRIORITY_PREFERENCES being enabled (which implies the user is signed in) is
50   // enough, and the sync mode doesn't matter.
51   if (base::FeatureList::IsEnabled(
52           syncer::kReplaceSyncPromosWithSignInPromos)) {
53     return true;
54   }
55 
56   // If `kReplaceSyncPromosWithSignInPromos` is NOT enabled, then demographics
57   // may only be uploaded for users who have opted in to Sync.
58   // TODO(crbug.com/40066949): Simplify once IsSyncFeatureEnabled() is deleted
59   // from the codebase.
60   if (sync_service->IsSyncFeatureEnabled()) {
61     return true;
62   }
63 
64   return false;
65 }
66 
67 }  // namespace
68 
69 // static
70 BASE_FEATURE(kDemographicMetricsReporting,
71              "DemographicMetricsReporting",
72              base::FEATURE_ENABLED_BY_DEFAULT);
73 
DemographicMetricsProvider(std::unique_ptr<ProfileClient> profile_client,MetricsLogUploader::MetricServiceType metrics_service_type)74 DemographicMetricsProvider::DemographicMetricsProvider(
75     std::unique_ptr<ProfileClient> profile_client,
76     MetricsLogUploader::MetricServiceType metrics_service_type)
77     : profile_client_(std::move(profile_client)),
78       metrics_service_type_(metrics_service_type) {
79   DCHECK(profile_client_);
80 }
81 
~DemographicMetricsProvider()82 DemographicMetricsProvider::~DemographicMetricsProvider() {}
83 
84 std::optional<UserDemographics>
ProvideSyncedUserNoisedBirthYearAndGender()85 DemographicMetricsProvider::ProvideSyncedUserNoisedBirthYearAndGender() {
86   // Skip if feature disabled.
87   if (!base::FeatureList::IsEnabled(kDemographicMetricsReporting))
88     return std::nullopt;
89 
90 #if !BUILDFLAG(IS_CHROMEOS_ASH)
91   // Skip if not exactly one Profile on disk. Having more than one Profile that
92   // is using the browser can make demographics less relevant. This approach
93   // cannot determine if there is more than 1 distinct user using the Profile.
94 
95   // ChromeOS almost always has more than one profile on disk, so this check
96   // doesn't work. We have a profile selection strategy for ChromeOS, so skip
97   // this check for ChromeOS.
98   // TODO(crbug/1145655): LaCros will behave similarly to desktop Chrome and
99   // reduce the number of profiles on disk to one, so remove these #if guards
100   // after LaCros release.
101   if (profile_client_->GetNumberOfProfilesOnDisk() != 1) {
102     LogUserDemographicsStatusInHistogram(
103         UserDemographicsStatus::kMoreThanOneProfile);
104     return std::nullopt;
105   }
106 #endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
107 
108   syncer::SyncService* sync_service = profile_client_->GetSyncService();
109   // Skip if no sync service.
110   if (!sync_service) {
111     LogUserDemographicsStatusInHistogram(
112         UserDemographicsStatus::kNoSyncService);
113     return std::nullopt;
114   }
115 
116   if (!CanUploadDemographicsToGoogle(sync_service)) {
117     LogUserDemographicsStatusInHistogram(
118         UserDemographicsStatus::kSyncNotEnabled);
119     return std::nullopt;
120   }
121 
122   UserDemographicsResult demographics_result =
123       GetUserNoisedBirthYearAndGenderFromPrefs(
124           profile_client_->GetNetworkTime(), profile_client_->GetLocalState(),
125           profile_client_->GetProfilePrefs());
126   LogUserDemographicsStatusInHistogram(demographics_result.status());
127 
128   if (demographics_result.IsSuccess())
129     return demographics_result.value();
130 
131   return std::nullopt;
132 }
133 
ProvideCurrentSessionData(ChromeUserMetricsExtension * uma_proto)134 void DemographicMetricsProvider::ProvideCurrentSessionData(
135     ChromeUserMetricsExtension* uma_proto) {
136   ProvideSyncedUserNoisedBirthYearAndGender(uma_proto);
137 }
138 
139 void DemographicMetricsProvider::
ProvideSyncedUserNoisedBirthYearAndGenderToReport(ukm::Report * report)140     ProvideSyncedUserNoisedBirthYearAndGenderToReport(ukm::Report* report) {
141   ProvideSyncedUserNoisedBirthYearAndGender(report);
142 }
143 
LogUserDemographicsStatusInHistogram(UserDemographicsStatus status)144 void DemographicMetricsProvider::LogUserDemographicsStatusInHistogram(
145     UserDemographicsStatus status) {
146   switch (metrics_service_type_) {
147     case MetricsLogUploader::MetricServiceType::UMA:
148       base::UmaHistogramEnumeration("UMA.UserDemographics.Status", status);
149       // If the user demographics data was retrieved successfully, then the user
150       // must be between the ages of |kUserDemographicsMinAgeInYears|+1=21 and
151       // |kUserDemographicsMaxAgeInYears|=85, so the user is not a minor.
152       base::UmaHistogramBoolean("UMA.UserDemographics.IsNoisedAgeOver21Under85",
153                                 status == UserDemographicsStatus::kSuccess);
154       return;
155     case MetricsLogUploader::MetricServiceType::UKM:
156       // UKM Metrics doesn't have demographic metrics.
157       return;
158     case MetricsLogUploader::MetricServiceType::STRUCTURED_METRICS:
159       // Structured Metrics doesn't have demographic metrics.
160       return;
161   }
162   NOTREACHED();
163 }
164 
165 }  // namespace metrics
166