blob: e5504ad872fc826f9c2b0ef690488ae6b36c87ea [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"
Sophie Chang647ee3a2023-06-06 15:43:4312#include "components/sync/service/sync_service.h"
Sophie Chang6045e222024-02-29 21:02:0913#include "components/sync/service/sync_service_utils.h"
Sophie Chang647ee3a2023-06-06 15:43:4314#include "components/unified_consent/consent_throttle.h"
15#include "components/unified_consent/url_keyed_data_collection_consent_helper.h"
16
17namespace page_image_service {
18
19namespace {
20
Sophie Changaae45dfc2023-09-11 18:42:2121void RunConsentThrottleCallback(
22 base::OnceCallback<void(PageImageServiceConsentStatus)> callback,
23 bool success) {
24 std::move(callback).Run(success ? PageImageServiceConsentStatus::kSuccess
25 : PageImageServiceConsentStatus::kFailure);
26}
27
Rushan Suleymanovb11a2972023-09-29 17:45:3528PageImageServiceConsentStatus ConsentStatusToUmaStatus(
Arthur Sonzognic571efb2024-01-26 20:26:1829 std::optional<bool> consent_status) {
Rushan Suleymanovb11a2972023-09-29 17:45:3530 if (!consent_status) {
31 return PageImageServiceConsentStatus::kTimedOut;
32 }
33 return consent_status.value() ? PageImageServiceConsentStatus::kSuccess
34 : PageImageServiceConsentStatus::kFailure;
35}
36
Sophie Chang647ee3a2023-06-06 15:43:4337} // namespace
38
39ImageServiceConsentHelper::ImageServiceConsentHelper(
40 syncer::SyncService* sync_service,
41 syncer::ModelType model_type)
Sophie Changdce8d9412023-09-29 05:33:3742 : sync_service_(sync_service),
43 model_type_(model_type),
44 timeout_duration_(base::Seconds(GetFieldTrialParamByFeatureAsInt(
45 kImageServiceObserveSyncDownloadStatus,
46 "timeout_seconds",
47 10))) {
Sophie Chang647ee3a2023-06-06 15:43:4348 if (base::FeatureList::IsEnabled(kImageServiceObserveSyncDownloadStatus)) {
49 sync_service_observer_.Observe(sync_service);
50 } else if (model_type == syncer::ModelType::BOOKMARKS) {
Mikel Astizdaff3182024-02-08 10:49:2951 // TODO(crbug.com/40067770): Migrate to require_sync_feature_enabled =
52 // false.
Sophie Chang647ee3a2023-06-06 15:43:4353 consent_throttle_ = std::make_unique<unified_consent::ConsentThrottle>(
54 unified_consent::UrlKeyedDataCollectionConsentHelper::
Boris Sazonov679ebbd2023-09-14 14:38:3355 NewPersonalizedBookmarksDataCollectionConsentHelper(
56 sync_service,
57 /*require_sync_feature_enabled=*/true),
Sophie Changdce8d9412023-09-29 05:33:3758 timeout_duration_);
Sophie Chang647ee3a2023-06-06 15:43:4359 } else if (model_type == syncer::ModelType::HISTORY_DELETE_DIRECTIVES) {
60 consent_throttle_ = std::make_unique<unified_consent::ConsentThrottle>(
61 unified_consent::UrlKeyedDataCollectionConsentHelper::
62 NewPersonalizedDataCollectionConsentHelper(sync_service),
Sophie Changdce8d9412023-09-29 05:33:3763 timeout_duration_);
Sophie Chang647ee3a2023-06-06 15:43:4364 } else {
65 NOTREACHED();
66 }
67}
68
69ImageServiceConsentHelper::~ImageServiceConsentHelper() = default;
70
71void ImageServiceConsentHelper::EnqueueRequest(
Rushan Suleymanove978a87d2023-10-12 16:39:0972 base::OnceCallback<void(PageImageServiceConsentStatus)> callback,
73 mojom::ClientId client_id) {
Rushan Suleymanovb11a2972023-09-29 17:45:3574 base::UmaHistogramBoolean("PageImageService.ConsentStatusRequestCount", true);
Sophie Chang647ee3a2023-06-06 15:43:4375 if (consent_throttle_) {
Sophie Changaae45dfc2023-09-11 18:42:2176 consent_throttle_->EnqueueRequest(
77 base::BindOnce(&RunConsentThrottleCallback, std::move(callback)));
Sophie Chang647ee3a2023-06-06 15:43:4378 return;
79 }
80
Arthur Sonzognic571efb2024-01-26 20:26:1881 std::optional<bool> consent_status = GetConsentStatus();
Sophie Chang647ee3a2023-06-06 15:43:4382 if (consent_status.has_value()) {
Sophie Changaae45dfc2023-09-11 18:42:2183 std::move(callback).Run(*consent_status
84 ? PageImageServiceConsentStatus::kSuccess
85 : PageImageServiceConsentStatus::kFailure);
Sophie Chang647ee3a2023-06-06 15:43:4386 return;
87 }
88
Rushan Suleymanove978a87d2023-10-12 16:39:0989 enqueued_request_callbacks_.emplace_back(std::move(callback), client_id);
Sophie Chang647ee3a2023-06-06 15:43:4390 if (!request_processing_timer_.IsRunning()) {
91 request_processing_timer_.Start(
Sophie Changdce8d9412023-09-29 05:33:3792 FROM_HERE, timeout_duration_,
Sophie Chang9fabf702023-08-11 22:19:0193 base::BindOnce(&ImageServiceConsentHelper::OnTimeoutExpired,
94 weak_ptr_factory_.GetWeakPtr()));
Sophie Chang647ee3a2023-06-06 15:43:4395 }
96}
97
98void ImageServiceConsentHelper::OnStateChanged(
99 syncer::SyncService* sync_service) {
100 CHECK_EQ(sync_service_, sync_service);
101 CHECK(base::FeatureList::IsEnabled(kImageServiceObserveSyncDownloadStatus));
102
Arthur Sonzognic571efb2024-01-26 20:26:18103 std::optional<bool> consent_status = GetConsentStatus();
Sophie Chang647ee3a2023-06-06 15:43:43104 if (!consent_status.has_value()) {
105 return;
106 }
107
Sophie Changeee5afeff2023-12-18 20:23:04108 request_processing_timer_.Stop();
109
110 // The request callbacks can modify the vector while running. Swap the vector
111 // onto the stack to prevent crashing. https://crbug.com/1472360.
112 std::vector<std::pair<base::OnceCallback<void(PageImageServiceConsentStatus)>,
113 mojom::ClientId>>
114 callbacks;
115 std::swap(callbacks, enqueued_request_callbacks_);
116 for (auto& request_callback_with_client_id : callbacks) {
Rushan Suleymanove978a87d2023-10-12 16:39:09117 std::move(request_callback_with_client_id.first)
Sophie Changaae45dfc2023-09-11 18:42:21118 .Run(*consent_status ? PageImageServiceConsentStatus::kSuccess
119 : PageImageServiceConsentStatus::kFailure);
Sophie Chang647ee3a2023-06-06 15:43:43120 }
Sophie Chang647ee3a2023-06-06 15:43:43121}
122
123void ImageServiceConsentHelper::OnSyncShutdown(
124 syncer::SyncService* sync_service) {
125 CHECK_EQ(sync_service_, sync_service);
126 CHECK(base::FeatureList::IsEnabled(kImageServiceObserveSyncDownloadStatus));
127
128 sync_service_observer_.Reset();
Sophie Changfba279122023-12-12 21:22:21129 sync_service_ = nullptr;
Sophie Chang647ee3a2023-06-06 15:43:43130}
131
Arthur Sonzognic571efb2024-01-26 20:26:18132std::optional<bool> ImageServiceConsentHelper::GetConsentStatus() {
Sophie Chang647ee3a2023-06-06 15:43:43133 CHECK(base::FeatureList::IsEnabled(kImageServiceObserveSyncDownloadStatus));
134
Sophie Changfba279122023-12-12 21:22:21135 if (!sync_service_) {
136 return false;
137 }
138
Sophie Chang6045e222024-02-29 21:02:09139 // If upload of the given ModelType is disabled (or inactive due to an
140 // error), then consent must be assumed to be NOT given.
141 // Note that the "INITIALIZING" state is good enough: It means the data
142 // type is enabled in principle, Sync just hasn't fully finished
143 // initializing yet. This case is handled by the DownloadStatus check
144 // below.
145 if (syncer::GetUploadToGoogleState(sync_service_, model_type_) ==
146 syncer::UploadState::NOT_ACTIVE) {
147 return false;
148 }
149
150 // Ensure Sync has downloaded all relevant updates (i.e. any deletions from
151 // other devices are known).
Sophie Chang647ee3a2023-06-06 15:43:43152 syncer::SyncService::ModelTypeDownloadStatus download_status =
153 sync_service_->GetDownloadStatusFor(model_type_);
154 switch (download_status) {
155 case syncer::SyncService::ModelTypeDownloadStatus::kWaitingForUpdates:
Arthur Sonzognic571efb2024-01-26 20:26:18156 return std::nullopt;
Sophie Chang647ee3a2023-06-06 15:43:43157 case syncer::SyncService::ModelTypeDownloadStatus::kUpToDate:
158 return true;
159 case syncer::SyncService::ModelTypeDownloadStatus::kError:
160 return false;
161 }
162}
163
164void ImageServiceConsentHelper::OnTimeoutExpired() {
Sophie Changeee5afeff2023-12-18 20:23:04165 // The request callbacks can modify the vector while running. Swap the vector
166 // onto the stack to prevent crashing. https://crbug.com/1472360.
167 std::vector<std::pair<base::OnceCallback<void(PageImageServiceConsentStatus)>,
168 mojom::ClientId>>
169 callbacks;
170 std::swap(callbacks, enqueued_request_callbacks_);
171 for (auto& request_callback_with_client_id : callbacks) {
Rushan Suleymanovb11a2972023-09-29 17:45:35172 // Report consent status on timeout for each request to compare against the
173 // number of all requests.
Sophie Changfba279122023-12-12 21:22:21174 PageImageServiceConsentStatus consent_status =
175 ConsentStatusToUmaStatus(GetConsentStatus());
Rushan Suleymanovb11a2972023-09-29 17:45:35176 base::UmaHistogramEnumeration("PageImageService.ConsentStatusOnTimeout",
Sophie Changfba279122023-12-12 21:22:21177 consent_status);
178 if (sync_service_) {
179 sync_service_->RecordReasonIfWaitingForUpdates(
180 model_type_, kConsentTimeoutReasonHistogramName);
181 sync_service_->RecordReasonIfWaitingForUpdates(
182 model_type_,
183 std::string(kConsentTimeoutReasonHistogramName) + "." +
184 ClientIdToString(request_callback_with_client_id.second));
185 }
186 std::move(request_callback_with_client_id.first).Run(consent_status);
Sophie Chang647ee3a2023-06-06 15:43:43187 }
Sophie Chang647ee3a2023-06-06 15:43:43188}
189
190} // namespace page_image_service