Commit 9bb51256 authored by Himanshu Jaju's avatar Himanshu Jaju Committed by Commit Bot

PayloadTracker implementation

Implements the payload tracker class which is responsible for relaying
progress updates in percent and maintaining status of incoming payloads.
Also ensures that connection is closed when a terminal status is reached.

Bug: 1085068
Change-Id: I329674a3e27784860955e25280024de678f49b5b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2362896Reviewed-by: default avatarAlex Chau <alexchau@chromium.org>
Commit-Queue: Himanshu Jaju <himanshujaju@chromium.org>
Cr-Commit-Position: refs/heads/master@{#800348}
parent 527876b8
......@@ -3417,6 +3417,8 @@ static_library("browser") {
"nearby_sharing/outgoing_share_target_info.h",
"nearby_sharing/paired_key_verification_runner.cc",
"nearby_sharing/paired_key_verification_runner.h",
"nearby_sharing/payload_tracker.cc",
"nearby_sharing/payload_tracker.h",
"nearby_sharing/share_target.cc",
"nearby_sharing/share_target.h",
"nearby_sharing/share_target_discovered_callback.h",
......
......@@ -24,6 +24,10 @@ constexpr base::TimeDelta kReadFramesTimeout = base::TimeDelta::FromSeconds(15);
constexpr base::TimeDelta kInvalidateDelay =
base::TimeDelta::FromMilliseconds(500);
// Time between successive progress updates.
constexpr base::TimeDelta kMinProgressUpdateFrequency =
base::TimeDelta::FromMilliseconds(100);
// If total size of all attachments is larger than this limit, online share will
// be disabled even if it would be allowed by the user.
constexpr int64_t kOnlineFileSizeLimitBytes = 25 * 1024 * 1024; // 25MB
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/nearby_sharing/payload_tracker.h"
#include "base/callback.h"
#include "chrome/browser/nearby_sharing/constants.h"
#include "chrome/browser/nearby_sharing/logging/logging.h"
#include "chrome/browser/nearby_sharing/transfer_metadata_builder.h"
PayloadTracker::PayloadTracker(
const ShareTarget& share_target,
const base::flat_map<int64_t, AttachmentInfo>& attachment_info_map,
base::RepeatingCallback<void(ShareTarget, TransferMetadata)>
update_callback)
: share_target_(share_target),
update_callback_(std::move(update_callback)) {
total_download_size_ = 0;
for (const auto& file : share_target.file_attachments) {
auto it = attachment_info_map.find(file.id());
if (it == attachment_info_map.end() || !it->second.payload_id) {
NS_LOG(WARNING)
<< __func__
<< ": Failed to retrieve payload for file attachment id - "
<< file.id();
continue;
}
payload_state_.emplace(*it->second.payload_id, State(file.size()));
total_download_size_ += file.size();
}
for (const auto& text : share_target.text_attachments) {
auto it = attachment_info_map.find(text.id());
if (it == attachment_info_map.end() || !it->second.payload_id) {
NS_LOG(WARNING)
<< __func__
<< ": Failed to retrieve payload for text attachment id - "
<< text.id();
continue;
}
payload_state_.emplace(*it->second.payload_id, State(text.size()));
total_download_size_ += text.size();
}
}
PayloadTracker::~PayloadTracker() = default;
void PayloadTracker::OnStatusUpdate(PayloadTransferUpdatePtr update) {
auto it = payload_state_.find(update->payload_id);
if (it == payload_state_.end())
return;
it->second.amount_downloaded = update->bytes_transferred;
it->second.status = update->status;
NS_LOG(VERBOSE) << __func__ << ": Payload id " << update->payload_id
<< " has status " << update->status;
OnTransferUpdate();
}
void PayloadTracker::OnTransferUpdate() {
if (IsComplete()) {
NS_LOG(VERBOSE) << __func__ << ": All payloads are complete.";
update_callback_.Run(share_target_,
TransferMetadataBuilder()
.set_status(TransferMetadata::Status::kComplete)
.build());
return;
}
if (IsCancelled()) {
NS_LOG(VERBOSE) << __func__ << ": Payloads cancelled.";
update_callback_.Run(share_target_,
TransferMetadataBuilder()
.set_status(TransferMetadata::Status::kCancelled)
.build());
return;
}
if (HasFailed()) {
NS_LOG(VERBOSE) << __func__ << ": Payloads failed.";
update_callback_.Run(share_target_,
TransferMetadataBuilder()
.set_status(TransferMetadata::Status::kFailed)
.build());
return;
}
double percent = CalculateProgressPercent();
int current_progress = static_cast<int>(percent * 100);
base::Time current_time = base::Time::Now();
if (current_progress == last_update_progress_ ||
(current_time - last_update_timestamp_) < kMinProgressUpdateFrequency) {
return;
}
NS_LOG(VERBOSE) << __func__ << ": Payloads are in progress at " << percent
<< " percent.";
last_update_progress_ = current_progress;
last_update_timestamp_ = current_time;
update_callback_.Run(share_target_,
TransferMetadataBuilder()
.set_status(TransferMetadata::Status::kInProgress)
.set_progress(percent)
.build());
}
bool PayloadTracker::IsComplete() {
for (const auto& state : payload_state_) {
if (state.second.status !=
location::nearby::connections::mojom::PayloadStatus::kSuccess) {
return false;
}
}
return true;
}
bool PayloadTracker::IsCancelled() {
for (const auto& state : payload_state_) {
if (state.second.status ==
location::nearby::connections::mojom::PayloadStatus::kCanceled) {
return true;
}
}
return false;
}
bool PayloadTracker::HasFailed() {
for (const auto& state : payload_state_) {
if (state.second.status ==
location::nearby::connections::mojom::PayloadStatus::kFailure) {
return true;
}
}
return false;
}
double PayloadTracker::CalculateProgressPercent() {
if (!total_download_size_) {
NS_LOG(WARNING) << __func__ << ": Total attachment size is 0";
return 100.0;
}
uint64_t total_downloaded = 0;
for (const auto& state : payload_state_)
total_downloaded += state.second.amount_downloaded;
return (100.0 * total_downloaded) / total_download_size_;
}
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_NEARBY_SHARING_PAYLOAD_TRACKER_H_
#define CHROME_BROWSER_NEARBY_SHARING_PAYLOAD_TRACKER_H_
#include "base/callback_forward.h"
#include "base/containers/flat_map.h"
#include "base/time/time.h"
#include "chrome/browser/nearby_sharing/attachment_info.h"
#include "chrome/browser/nearby_sharing/nearby_connections_manager.h"
#include "chrome/browser/nearby_sharing/share_target.h"
#include "chrome/browser/nearby_sharing/transfer_metadata.h"
#include "chrome/services/sharing/public/mojom/nearby_connections_types.mojom.h"
class PayloadTracker : public NearbyConnectionsManager::PayloadStatusListener {
public:
PayloadTracker(
const ShareTarget& share_target,
const base::flat_map<int64_t, AttachmentInfo>& attachment_info_map,
base::RepeatingCallback<void(ShareTarget, TransferMetadata)>
update_callback);
~PayloadTracker() override;
// NearbyConnectionsManager::PayloadStatusListener:
void OnStatusUpdate(PayloadTransferUpdatePtr update) override;
private:
struct State {
explicit State(int64_t total_size) : total_size(total_size) {}
~State() = default;
uint64_t amount_downloaded = 0;
const uint64_t total_size;
location::nearby::connections::mojom::PayloadStatus status =
location::nearby::connections::mojom::PayloadStatus::kInProgress;
};
void OnTransferUpdate();
bool IsComplete();
bool IsCancelled();
bool HasFailed();
double CalculateProgressPercent();
ShareTarget share_target_;
base::RepeatingCallback<void(ShareTarget, TransferMetadata)> update_callback_;
// Map of payload id to state of payload.
std::map<int64_t, State> payload_state_;
uint64_t total_download_size_;
int last_update_progress_ = 0;
base::Time last_update_timestamp_;
};
#endif // CHROME_BROWSER_NEARBY_SHARING_PAYLOAD_TRACKER_H_
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/nearby_sharing/payload_tracker.h"
#include <memory>
#include "base/test/mock_callback.h"
#include "chrome/browser/nearby_sharing/constants.h"
#include "chrome/browser/nearby_sharing/transfer_metadata_builder.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
// Total number of attachments.
constexpr int kAttachmentCount = 4;
// Size of each attachment.
constexpr int kTotalSize = 10;
} // namespace
MATCHER_P(MetadataMatcher, expected_metadata, "") {
return expected_metadata.status() == arg.status() &&
expected_metadata.progress() == arg.progress();
}
class PayloadTrackerTest : public testing::Test {
public:
using MockUpdateCallback = base::MockCallback<
base::RepeatingCallback<void(ShareTarget, TransferMetadata)>>;
PayloadTrackerTest()
: task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
~PayloadTrackerTest() override = default;
void SetUp() override {
for (int i = 0; i < kAttachmentCount / 2; i++) {
FileAttachment file(/*file_name=*/"file.jpg",
FileAttachment::Type::kImage, kTotalSize,
/*file_path=*/base::nullopt,
/*mime_type=*/"example");
AttachmentInfo info;
info.payload_id = i;
attachment_info_map_.emplace(file.id(), std::move(info));
share_target_.file_attachments.push_back(std::move(file));
}
for (int i = kAttachmentCount / 2; i < kAttachmentCount; i++) {
TextAttachment text(TextAttachment::Type::kText, "text body.");
AttachmentInfo info;
info.payload_id = i;
attachment_info_map_.emplace(text.id(), std::move(info));
share_target_.text_attachments.push_back(std::move(text));
}
// This attachment is not added to |attachment_info_map_|.
TextAttachment text(TextAttachment::Type::kText, "text body.");
share_target_.text_attachments.push_back(std::move(text));
payload_tracker_ = std::make_unique<PayloadTracker>(
share_target_, attachment_info_map_, callback_.Get());
}
MockUpdateCallback& callback() { return callback_; }
void MarkSuccessful(int payload_id) {
UpdatePayloadStatus(
payload_id,
location::nearby::connections::mojom::PayloadStatus::kSuccess);
}
void MarkCancelled(int payload_id) {
UpdatePayloadStatus(
payload_id,
location::nearby::connections::mojom::PayloadStatus::kCanceled);
}
void MarkFailure(int payload_id) {
UpdatePayloadStatus(
payload_id,
location::nearby::connections::mojom::PayloadStatus::kFailure);
}
void WaitBetweenUpdates() {
task_environment_.FastForwardBy(kMinProgressUpdateFrequency);
}
private:
void UpdatePayloadStatus(
int payload_id,
location::nearby::connections::mojom::PayloadStatus status) {
location::nearby::connections::mojom::PayloadTransferUpdatePtr payload =
location::nearby::connections::mojom::PayloadTransferUpdate::New(
payload_id, status,
/*total_bytes=*/kTotalSize, /*bytes_transferred=*/kTotalSize);
payload_tracker_->OnStatusUpdate(std::move(payload));
}
content::BrowserTaskEnvironment task_environment_;
ShareTarget share_target_;
base::flat_map<int64_t, AttachmentInfo> attachment_info_map_;
MockUpdateCallback callback_;
std::unique_ptr<PayloadTracker> payload_tracker_;
};
// Tests update callback when all payloads are completed.
TEST_F(PayloadTrackerTest, PayloadsComplete_Successful) {
EXPECT_CALL(
callback(),
Run(testing::_,
MetadataMatcher(TransferMetadataBuilder()
.set_status(TransferMetadata::Status::kInProgress)
.set_progress(25)
.build())));
EXPECT_CALL(
callback(),
Run(testing::_,
MetadataMatcher(TransferMetadataBuilder()
.set_status(TransferMetadata::Status::kInProgress)
.set_progress(50)
.build())));
EXPECT_CALL(
callback(),
Run(testing::_,
MetadataMatcher(TransferMetadataBuilder()
.set_status(TransferMetadata::Status::kInProgress)
.set_progress(75)
.build())));
EXPECT_CALL(
callback(),
Run(testing::_,
MetadataMatcher(TransferMetadataBuilder()
.set_status(TransferMetadata::Status::kComplete)
.build())));
for (int payload_id = 0; payload_id < kAttachmentCount; payload_id++) {
MarkSuccessful(payload_id);
WaitBetweenUpdates();
}
}
// Tests update callback when one of the payload fails.
TEST_F(PayloadTrackerTest, PayloadsComplete_PartialFailure) {
EXPECT_CALL(
callback(),
Run(testing::_,
MetadataMatcher(TransferMetadataBuilder()
.set_status(TransferMetadata::Status::kFailed)
.build())));
MarkFailure(/*payload_id=*/0);
}
// Tests update callback when one of the payload gets cancelled.
TEST_F(PayloadTrackerTest, PayloadsComplete_PartialCancelled) {
EXPECT_CALL(
callback(),
Run(testing::_,
MetadataMatcher(TransferMetadataBuilder()
.set_status(TransferMetadata::Status::kCancelled)
.build())));
MarkCancelled(/*payload_id=*/1);
}
// Tests update callback when the payloads are still being received.
TEST_F(PayloadTrackerTest, PayloadsInProgress) {
EXPECT_CALL(
callback(),
Run(testing::_,
MetadataMatcher(TransferMetadataBuilder()
.set_status(TransferMetadata::Status::kInProgress)
.set_progress(25)
.build())));
MarkSuccessful(/*payload_id=*/0);
}
// Tests update callback when a status update is received, but there is no
// change in the percentage of received data.
TEST_F(PayloadTrackerTest, MultipleInProgressUpdates_SamePercentage) {
EXPECT_CALL(
callback(),
Run(testing::_,
MetadataMatcher(TransferMetadataBuilder()
.set_status(TransferMetadata::Status::kInProgress)
.set_progress(25)
.build())));
MarkSuccessful(/*payload_id=*/0);
WaitBetweenUpdates();
MarkSuccessful(/*payload_id=*/0);
}
// Tests update callback when a status update is received in almost the same
// time as the last update.
TEST_F(PayloadTrackerTest, MultipleInProgressUpdates_HighFrequency) {
EXPECT_CALL(
callback(),
Run(testing::_,
MetadataMatcher(TransferMetadataBuilder()
.set_status(TransferMetadata::Status::kInProgress)
.set_progress(25)
.build())));
MarkSuccessful(/*payload_id=*/0);
MarkSuccessful(/*payload_id=*/1);
}
// Tests update callback when a status update for an unknown payload.
TEST_F(PayloadTrackerTest, StatusUpdateForUnknownPayload) {
EXPECT_CALL(callback(), Run(testing::_, testing::_)).Times(0);
MarkSuccessful(/*payload_id=*/4);
}
......@@ -12,6 +12,7 @@
#include "chrome/browser/nearby_sharing/certificates/nearby_share_decrypted_public_certificate.h"
#include "chrome/browser/nearby_sharing/incoming_frames_reader.h"
#include "chrome/browser/nearby_sharing/paired_key_verification_runner.h"
#include "chrome/browser/nearby_sharing/payload_tracker.h"
class NearbyConnection;
......@@ -65,6 +66,12 @@ class ShareTargetInfo {
key_verification_runner_ = std::move(key_verification_runner);
}
PayloadTracker* payload_tracker() { return payload_tracker_.get(); }
void set_payload_tracker(std::unique_ptr<PayloadTracker> payload_tracker) {
payload_tracker_ = std::move(payload_tracker);
}
private:
base::Optional<std::string> endpoint_id_;
base::Optional<NearbyShareDecryptedPublicCertificate> certificate_;
......@@ -72,6 +79,7 @@ class ShareTargetInfo {
base::Optional<std::string> token_;
std::unique_ptr<IncomingFramesReader> frames_reader_;
std::unique_ptr<PairedKeyVerificationRunner> key_verification_runner_;
std::unique_ptr<PayloadTracker> payload_tracker_;
};
#endif // CHROME_BROWSER_NEARBY_SHARING_SHARE_TARGET_INFO_H_
......@@ -46,6 +46,8 @@ class TransferMetadata {
TransferMetadata& operator=(const TransferMetadata&);
Status status() const { return status_; }
// Returns transfer progress as percentage.
float progress() const { return progress_; }
// Represents the UKey2 token from Nearby Connection. base::nullopt if no
......
......@@ -3771,6 +3771,7 @@ test("unit_tests") {
"../browser/nearby_sharing/nearby_share_settings_unittest.cc",
"../browser/nearby_sharing/nearby_sharing_service_impl_unittest.cc",
"../browser/nearby_sharing/paired_key_verification_runner_unittest.cc",
"../browser/nearby_sharing/payload_tracker_unittest.cc",
"../browser/nearby_sharing/webrtc_signaling_messenger_unittest.cc",
"../browser/password_manager/generated_password_leak_detection_pref_unittest.cc",
"../browser/performance_manager/test_support/page_discarding_utils.cc",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment