| // 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 |