blob: 0a8e2381562262f1aa8f1ef85da21015d7edbb93 [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/private_aggregation/private_aggregation_manager_impl.h"
#include <memory>
#include <numeric>
#include <string>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/callback.h"
#include "base/check.h"
#include "base/files/file_path.h"
#include "base/memory/scoped_refptr.h"
#include "base/metrics/histogram_functions.h"
#include "base/numerics/checked_math.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/task/updateable_sequenced_task_runner.h"
#include "base/time/time.h"
#include "content/browser/aggregation_service/aggregatable_report.h"
#include "content/browser/aggregation_service/aggregation_service.h"
#include "content/browser/private_aggregation/private_aggregation_budget_key.h"
#include "content/browser/private_aggregation/private_aggregation_budgeter.h"
#include "content/browser/private_aggregation/private_aggregation_host.h"
#include "content/browser/private_aggregation/private_aggregation_utils.h"
#include "content/browser/storage_partition_impl.h"
#include "content/common/aggregatable_report.mojom.h"
#include "content/common/private_aggregation_host.mojom.h"
#include "content/public/browser/storage_partition.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "url/origin.h"
namespace content {
namespace {
void RecordBudgeterResultHistogram(
PrivateAggregationBudgeter::RequestResult request_result) {
base::UmaHistogramEnumeration(
"PrivacySandbox.PrivateAggregation.Budgeter.RequestResult",
request_result);
}
} // namespace
PrivateAggregationManagerImpl::PrivateAggregationManagerImpl(
bool exclusively_run_in_memory,
const base::FilePath& user_data_directory,
StoragePartitionImpl* storage_partition)
: PrivateAggregationManagerImpl(
std::make_unique<PrivateAggregationBudgeter>(
// This uses BLOCK_SHUTDOWN as some data deletion operations may
// be running when the browser is closed, and we want to ensure
// all data is deleted correctly. Additionally, we use
// MUST_USE_FOREGROUND to avoid priority inversions if a task is
// already running when the priority is increased.
base::ThreadPool::CreateUpdateableSequencedTaskRunner(
base::TaskTraits(base::TaskPriority::BEST_EFFORT,
base::MayBlock(),
base::TaskShutdownBehavior::BLOCK_SHUTDOWN,
base::ThreadPolicy::MUST_USE_FOREGROUND)),
exclusively_run_in_memory,
/*path_to_db_dir=*/user_data_directory),
std::make_unique<PrivateAggregationHost>(
/*on_report_request_received=*/base::BindRepeating(
&PrivateAggregationManagerImpl::
OnReportRequestReceivedFromHost,
base::Unretained(this)),
storage_partition ? storage_partition->browser_context()
: nullptr),
storage_partition) {}
PrivateAggregationManagerImpl::PrivateAggregationManagerImpl(
std::unique_ptr<PrivateAggregationBudgeter> budgeter,
std::unique_ptr<PrivateAggregationHost> host,
StoragePartitionImpl* storage_partition)
: budgeter_(std::move(budgeter)),
host_(std::move(host)),
storage_partition_(storage_partition) {
DCHECK(budgeter_);
DCHECK(host_);
}
PrivateAggregationManagerImpl::~PrivateAggregationManagerImpl() = default;
bool PrivateAggregationManagerImpl::BindNewReceiver(
url::Origin worklet_origin,
url::Origin top_frame_origin,
PrivateAggregationBudgetKey::Api api_for_budgeting,
mojo::PendingReceiver<mojom::PrivateAggregationHost> pending_receiver) {
return host_->BindNewReceiver(std::move(worklet_origin),
std::move(top_frame_origin), api_for_budgeting,
std::move(pending_receiver));
}
void PrivateAggregationManagerImpl::ClearBudgetData(
base::Time delete_begin,
base::Time delete_end,
StoragePartition::StorageKeyMatcherFunction filter,
base::OnceClosure done) {
budgeter_->ClearData(delete_begin, delete_end, std::move(filter),
std::move(done));
}
void PrivateAggregationManagerImpl::OnReportRequestReceivedFromHost(
AggregatableReportRequest report_request,
PrivateAggregationBudgetKey budget_key) {
const std::vector<mojom::AggregatableReportHistogramContribution>&
contributions = report_request.payload_contents().contributions;
base::CheckedNumeric<int> budget_needed = std::accumulate(
contributions.begin(), contributions.end(),
/*init=*/base::CheckedNumeric<int>(0), /*op=*/
[](base::CheckedNumeric<int> running_sum,
const mojom::AggregatableReportHistogramContribution& contribution) {
return running_sum + contribution.value;
});
if (!budget_needed.IsValid()) {
RecordBudgeterResultHistogram(PrivateAggregationBudgeter::RequestResult::
kRequestedMoreThanTotalBudget);
return;
}
PrivateAggregationBudgetKey::Api api_for_budgeting = budget_key.api();
budgeter_->ConsumeBudget(
budget_needed.ValueOrDie(), std::move(budget_key), /*on_done=*/
// Unretained is safe as the `budgeter_` is owned by `this`.
base::BindOnce(&PrivateAggregationManagerImpl::OnConsumeBudgetReturned,
base::Unretained(this), std::move(report_request),
api_for_budgeting));
}
AggregationService* PrivateAggregationManagerImpl::GetAggregationService() {
DCHECK(storage_partition_);
return AggregationService::GetService(storage_partition_->browser_context());
}
void PrivateAggregationManagerImpl::OnConsumeBudgetReturned(
AggregatableReportRequest report_request,
PrivateAggregationBudgetKey::Api api_for_budgeting,
PrivateAggregationBudgeter::RequestResult request_result) {
RecordBudgeterResultHistogram(request_result);
if (request_result != PrivateAggregationBudgeter::RequestResult::kApproved) {
return;
}
AggregationService* aggregation_service = GetAggregationService();
if (!aggregation_service) {
return;
}
// If the request has debug mode enabled, immediately send a duplicate of the
// requested report to a special debug reporting endpoint.
if (report_request.shared_info().debug_mode ==
AggregatableReportSharedInfo::DebugMode::kEnabled) {
std::string immediate_debug_reporting_path =
private_aggregation::GetReportingPath(
api_for_budgeting,
/*is_immediate_debug_report=*/true);
absl::optional<AggregatableReportRequest> debug_request =
AggregatableReportRequest::Create(
report_request.payload_contents(),
report_request.shared_info().Clone(),
std::move(immediate_debug_reporting_path),
report_request.debug_key());
DCHECK(debug_request.has_value());
aggregation_service->AssembleAndSendReport(
std::move(debug_request.value()));
}
aggregation_service->ScheduleReport(std::move(report_request));
}
} // namespace content