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