blob: 9098a215fbab1605f8af3f5901774a026c6b65ce [file] [log] [blame]
Sophie Chang647ee3a2023-06-06 15:43:431// Copyright 2023 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/page_image_service/image_service_consent_helper.h"
6
7#include "base/feature_list.h"
Sophie Changf47da292023-09-20 18:30:208#include "base/metrics/field_trial_params.h"
Rushan Suleymanovb11a2972023-09-29 17:45:359#include "base/metrics/histogram_functions.h"
Sophie Chang647ee3a2023-06-06 15:43:4310#include "components/page_image_service/features.h"
Sophie Changaae45dfc2023-09-11 18:42:2111#include "components/page_image_service/metrics_util.h"
Marc Treibb85a96f2024-03-11 18:01:0312#include "components/sync/base/features.h"
Sophie Chang647ee3a2023-06-06 15:43:4313#include "components/sync/service/sync_service.h"
Sophie Chang6045e222024-02-29 21:02:0914#include "components/sync/service/sync_service_utils.h"
Sophie Chang647ee3a2023-06-06 15:43:4315#include "components/unified_consent/consent_throttle.h"
16#include "components/unified_consent/url_keyed_data_collection_consent_helper.h"
17
18namespace page_image_service {
19
20namespace {
21
Sophie Changaae45dfc2023-09-11 18:42:2122void RunConsentThrottleCallback(
23 base::OnceCallback<void(PageImageServiceConsentStatus)> callback,
24 bool success) {
25 std::move(callback).Run(success ? PageImageServiceConsentStatus::kSuccess
26 : PageImageServiceConsentStatus::kFailure);
27}
28
Rushan Suleymanovb11a2972023-09-29 17:45:3529PageImageServiceConsentStatus ConsentStatusToUmaStatus(
Arthur Sonzognic571efb2024-01-26 20:26:1830 std::optional<bool> consent_status) {
Rushan Suleymanovb11a2972023-09-29 17:45:3531 if (!consent_status) {
32 return PageImageServiceConsentStatus::kTimedOut;
33 }
34 return consent_status.value() ? PageImageServiceConsentStatus::kSuccess
35 : PageImageServiceConsentStatus::kFailure;
36}
37
Sophie Chang647ee3a2023-06-06 15:43:4338} // namespace
39
40ImageServiceConsentHelper::ImageServiceConsentHelper(
41 syncer::SyncService* sync_service,
42 syncer::ModelType model_type)
Sophie Changdce8d9412023-09-29 05:33:3743 : sync_service_(sync_service),
44 model_type_(model_type),
45 timeout_duration_(base::Seconds(GetFieldTrialParamByFeatureAsInt(
46 kImageServiceObserveSyncDownloadStatus,
47 "timeout_seconds",
48 10))) {
Sophie Chang647ee3a2023-06-06 15:43:4349 if (base::FeatureList::IsEnabled(kImageServiceObserveSyncDownloadStatus)) {
50 sync_service_observer_.Observe(sync_service);
51 } else if (model_type == syncer::ModelType::BOOKMARKS) {
Sophie Chang647ee3a2023-06-06 15:43:4352 consent_throttle_ = std::make_unique<unified_consent::ConsentThrottle>(
53 unified_consent::UrlKeyedDataCollectionConsentHelper::
Boris Sazonov679ebbd2023-09-14 14:38:3354 NewPersonalizedBookmarksDataCollectionConsentHelper(
55 sync_service,
Marc Treibb85a96f2024-03-11 18:01:0356 /*require_sync_feature_enabled=*/!base::FeatureList::IsEnabled(
57 syncer::kReplaceSyncPromosWithSignInPromos) &&
58 !base::FeatureList::IsEnabled(
Mikel Astiz1a50ac272024-06-24 17:54:5159 syncer::kSyncEnableBookmarksInTransportMode)),
Sophie Changdce8d9412023-09-29 05:33:3760 timeout_duration_);
Sophie Chang647ee3a2023-06-06 15:43:4361 } else if (model_type == syncer::ModelType::HISTORY_DELETE_DIRECTIVES) {
62 consent_throttle_ = std::make_unique<unified_consent::ConsentThrottle>(
63 unified_consent::UrlKeyedDataCollectionConsentHelper::
64 NewPersonalizedDataCollectionConsentHelper(sync_service),
Sophie Changdce8d9412023-09-29 05:33:3765 timeout_duration_);
Sophie Chang647ee3a2023-06-06 15:43:4366 } else {
Peter Boströmaaf19db2024-05-14 22:08:0967 NOTREACHED_IN_MIGRATION();
Sophie Chang647ee3a2023-06-06 15:43:4368 }
69}
70
71ImageServiceConsentHelper::~ImageServiceConsentHelper() = default;
72
73void ImageServiceConsentHelper::EnqueueRequest(
Rushan Suleymanove978a87d2023-10-12 16:39:0974 base::OnceCallback<void(PageImageServiceConsentStatus)> callback,
75 mojom::ClientId client_id) {
Rushan Suleymanovb11a2972023-09-29 17:45:3576 base::UmaHistogramBoolean("PageImageService.ConsentStatusRequestCount", true);
Sophie Chang647ee3a2023-06-06 15:43:4377 if (consent_throttle_) {
Sophie Changaae45dfc2023-09-11 18:42:2178 consent_throttle_->EnqueueRequest(
79 base::BindOnce(&RunConsentThrottleCallback, std::move(callback)));
Sophie Chang647ee3a2023-06-06 15:43:4380 return;
81 }
82
Arthur Sonzognic571efb2024-01-26 20:26:1883 std::optional<bool> consent_status = GetConsentStatus();
Sophie Chang647ee3a2023-06-06 15:43:4384 if (consent_status.has_value()) {
Sophie Changaae45dfc2023-09-11 18:42:2185 std::move(callback).Run(*consent_status
86 ? PageImageServiceConsentStatus::kSuccess
87 : PageImageServiceConsentStatus::kFailure);
Sophie Chang647ee3a2023-06-06 15:43:4388 return;
89 }
90
Rushan Suleymanove978a87d2023-10-12 16:39:0991 enqueued_request_callbacks_.emplace_back(std::move(callback), client_id);
Sophie Chang647ee3a2023-06-06 15:43:4392 if (!request_processing_timer_.IsRunning()) {
93 request_processing_timer_.Start(
Sophie Changdce8d9412023-09-29 05:33:3794 FROM_HERE, timeout_duration_,
Sophie Chang9fabf702023-08-11 22:19:0195 base::BindOnce(&ImageServiceConsentHelper::OnTimeoutExpired,
96 weak_ptr_factory_.GetWeakPtr()));
Sophie Chang647ee3a2023-06-06 15:43:4397 }
98}
99
100void ImageServiceConsentHelper::OnStateChanged(
101 syncer::SyncService* sync_service) {
102 CHECK_EQ(sync_service_, sync_service);
103 CHECK(base::FeatureList::IsEnabled(kImageServiceObserveSyncDownloadStatus));
104
Arthur Sonzognic571efb2024-01-26 20:26:18105 std::optional<bool> consent_status = GetConsentStatus();
Sophie Chang647ee3a2023-06-06 15:43:43106 if (!consent_status.has_value()) {
107 return;
108 }
109
Sophie Changeee5afeff2023-12-18 20:23:04110 request_processing_timer_.Stop();
111
112 // The request callbacks can modify the vector while running. Swap the vector
113 // onto the stack to prevent crashing. https://crbug.com/1472360.
114 std::vector<std::pair<base::OnceCallback<void(PageImageServiceConsentStatus)>,
115 mojom::ClientId>>
116 callbacks;
117 std::swap(callbacks, enqueued_request_callbacks_);
118 for (auto& request_callback_with_client_id : callbacks) {
Rushan Suleymanove978a87d2023-10-12 16:39:09119 std::move(request_callback_with_client_id.first)
Sophie Changaae45dfc2023-09-11 18:42:21120 .Run(*consent_status ? PageImageServiceConsentStatus::kSuccess
121 : PageImageServiceConsentStatus::kFailure);
Sophie Chang647ee3a2023-06-06 15:43:43122 }
Sophie Chang647ee3a2023-06-06 15:43:43123}
124
125void ImageServiceConsentHelper::OnSyncShutdown(
126 syncer::SyncService* sync_service) {
127 CHECK_EQ(sync_service_, sync_service);
128 CHECK(base::FeatureList::IsEnabled(kImageServiceObserveSyncDownloadStatus));
129
130 sync_service_observer_.Reset();
Sophie Changfba279122023-12-12 21:22:21131 sync_service_ = nullptr;
Sophie Chang647ee3a2023-06-06 15:43:43132}
133
Arthur Sonzognic571efb2024-01-26 20:26:18134std::optional<bool> ImageServiceConsentHelper::GetConsentStatus() {
Sophie Chang647ee3a2023-06-06 15:43:43135 CHECK(base::FeatureList::IsEnabled(kImageServiceObserveSyncDownloadStatus));
136
Sophie Changfba279122023-12-12 21:22:21137 if (!sync_service_) {
138 return false;
139 }
140
Sophie Chang6045e222024-02-29 21:02:09141 // If upload of the given ModelType is disabled (or inactive due to an
142 // error), then consent must be assumed to be NOT given.
143 // Note that the "INITIALIZING" state is good enough: It means the data
144 // type is enabled in principle, Sync just hasn't fully finished
145 // initializing yet. This case is handled by the DownloadStatus check
146 // below.
147 if (syncer::GetUploadToGoogleState(sync_service_, model_type_) ==
148 syncer::UploadState::NOT_ACTIVE) {
149 return false;
150 }
151
152 // Ensure Sync has downloaded all relevant updates (i.e. any deletions from
153 // other devices are known).
Sophie Chang647ee3a2023-06-06 15:43:43154 syncer::SyncService::ModelTypeDownloadStatus download_status =
155 sync_service_->GetDownloadStatusFor(model_type_);
156 switch (download_status) {
157 case syncer::SyncService::ModelTypeDownloadStatus::kWaitingForUpdates:
Arthur Sonzognic571efb2024-01-26 20:26:18158 return std::nullopt;
Sophie Chang647ee3a2023-06-06 15:43:43159 case syncer::SyncService::ModelTypeDownloadStatus::kUpToDate:
160 return true;
161 case syncer::SyncService::ModelTypeDownloadStatus::kError:
162 return false;
163 }
164}
165
166void ImageServiceConsentHelper::OnTimeoutExpired() {
Sophie Changeee5afeff2023-12-18 20:23:04167 // The request callbacks can modify the vector while running. Swap the vector
168 // onto the stack to prevent crashing. https://crbug.com/1472360.
169 std::vector<std::pair<base::OnceCallback<void(PageImageServiceConsentStatus)>,
170 mojom::ClientId>>
171 callbacks;
172 std::swap(callbacks, enqueued_request_callbacks_);
173 for (auto& request_callback_with_client_id : callbacks) {
Rushan Suleymanovb11a2972023-09-29 17:45:35174 // Report consent status on timeout for each request to compare against the
175 // number of all requests.
Sophie Changfba279122023-12-12 21:22:21176 PageImageServiceConsentStatus consent_status =
177 ConsentStatusToUmaStatus(GetConsentStatus());
Rushan Suleymanovb11a2972023-09-29 17:45:35178 base::UmaHistogramEnumeration("PageImageService.ConsentStatusOnTimeout",
Sophie Changfba279122023-12-12 21:22:21179 consent_status);
180 if (sync_service_) {
181 sync_service_->RecordReasonIfWaitingForUpdates(
182 model_type_, kConsentTimeoutReasonHistogramName);
183 sync_service_->RecordReasonIfWaitingForUpdates(
184 model_type_,
185 std::string(kConsentTimeoutReasonHistogramName) + "." +
186 ClientIdToString(request_callback_with_client_id.second));
187 }
188 std::move(request_callback_with_client_id.first).Run(consent_status);
Sophie Chang647ee3a2023-06-06 15:43:43189 }
Sophie Chang647ee3a2023-06-06 15:43:43190}
191
192} // namespace page_image_service