blob: c79292b69022431bf553f3f093bdff15844351f0 [file] [log] [blame] [edit]
// Copyright 2024 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "missive/storage/key_delivery.h"
#include <memory>
#include <utility>
#include <vector>
#include <base/functional/callback_forward.h>
#include <base/functional/callback_helpers.h>
#include <base/logging.h>
#include <base/task/bind_post_task.h>
#include <base/task/sequenced_task_runner.h>
#include <base/task/thread_pool.h>
#include <base/thread_annotations.h>
#include <base/time/time.h>
#include <base/timer/timer.h>
#include "missive/analytics/metrics.h"
#include "missive/encryption/encryption_module_interface.h"
#include "missive/storage/storage_uploader_interface.h"
#include "missive/util/status.h"
namespace reporting {
// Factory method, returns smart pointer with deletion on sequence.
std::unique_ptr<KeyDelivery, base::OnTaskRunnerDeleter> KeyDelivery::Create(
base::TimeDelta key_check_period,
scoped_refptr<EncryptionModuleInterface> encryption_module,
UploaderInterface::AsyncStartUploaderCb async_start_upload_cb) {
auto sequence_task_runner = base::ThreadPool::CreateSequencedTaskRunner(
{base::TaskPriority::BEST_EFFORT, base::MayBlock()});
return std::unique_ptr<KeyDelivery, base::OnTaskRunnerDeleter>(
new KeyDelivery(key_check_period, encryption_module,
async_start_upload_cb, sequence_task_runner),
base::OnTaskRunnerDeleter(sequence_task_runner));
}
KeyDelivery::~KeyDelivery() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
upload_timer_.Stop();
PostResponses(
Status(error::UNAVAILABLE, "Key not delivered - Storage shuts down"));
}
void KeyDelivery::Request(RequestCallback callback) {
StartPeriodicKeyUpdate();
sequenced_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&KeyDelivery::EnqueueRequestAndPossiblyStart,
base::Unretained(this), std::move(callback)));
}
void KeyDelivery::OnKeyUpdateResult(Status status) {
// Log the request status in UMA.
const auto res = analytics::Metrics::SendEnumToUMA(
/*name=*/KeyDelivery::kResultUma, status.code(), error::Code::MAX_VALUE);
LOG_IF(ERROR, !res) << "SendEnumToUMA failure, " << KeyDelivery::kResultUma
<< " " << static_cast<int>(status.code());
sequenced_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&KeyDelivery::PostResponses,
base::Unretained(this), status));
}
void KeyDelivery::StartPeriodicKeyUpdate() {
sequenced_task_runner_->PostTask(
FROM_HERE, base::BindOnce(
[](KeyDelivery* self) {
DCHECK_CALLED_ON_VALID_SEQUENCE(self->sequence_checker_);
if (self->upload_timer_.IsRunning()) {
// We've already started the periodic key update.
return;
}
// `base::Unretained` is ok here because `upload_timer_`
// is destructed in the class destructor, and so is the
// callback.
self->upload_timer_.Start(
FROM_HERE, self->key_check_period_,
base::BindRepeating(&KeyDelivery::RequestKeyIfNeeded,
base::Unretained(self)));
},
base::Unretained(this)));
}
KeyDelivery::KeyDelivery(
base::TimeDelta key_check_period,
scoped_refptr<EncryptionModuleInterface> encryption_module,
UploaderInterface::AsyncStartUploaderCb async_start_upload_cb,
scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner)
: sequenced_task_runner_(sequenced_task_runner),
key_check_period_(key_check_period),
async_start_upload_cb_(async_start_upload_cb),
encryption_module_(encryption_module) {
CHECK(encryption_module_) << "Encryption module pointer not set";
DETACH_FROM_SEQUENCE(sequence_checker_);
}
void KeyDelivery::RequestKeyIfNeeded() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (encryption_module_->has_encryption_key() &&
!encryption_module_->need_encryption_key()) {
return;
}
// Request the key, do not expect any callback.
Request(base::NullCallback());
}
void KeyDelivery::EnqueueRequestAndPossiblyStart(RequestCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (callback) {
callbacks_.push_back(std::move(callback));
}
// Initiate upload with need_encryption_key flag and no records.
UploaderInterface::UploaderInterfaceResultCb start_uploader_cb =
base::BindOnce(&KeyDelivery::EncryptionKeyReceiverReady,
base::Unretained(this));
async_start_upload_cb_.Run(UploaderInterface::UploadReason::KEY_DELIVERY,
/*inform_cb=*/base::DoNothing(), // No records.
std::move(start_uploader_cb));
}
void KeyDelivery::PostResponses(Status status) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
for (auto& callback : callbacks_) {
std::move(callback).Run(status);
}
callbacks_.clear();
}
void KeyDelivery::EncryptionKeyReceiverReady(
StatusOr<std::unique_ptr<UploaderInterface>> uploader_result) {
if (!uploader_result.has_value()) {
OnKeyUpdateResult(uploader_result.error());
return;
}
uploader_result.value()->Completed(Status::StatusOK());
}
} // namespace reporting