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