blob: 233bd08165978d20f4172c45008304665a3c1132 [file] [log] [blame]
// Copyright (c) 2018 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/media/webrtc/webrtc_event_log_manager_remote.h"
#include <algorithm>
#include <iterator>
#include <limits>
#include "base/big_endian.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/files/file.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/rand_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "chrome/common/chrome_switches.h"
#include "content/public/browser/browser_thread.h"
const size_t kMaxRemoteLogFileMetadataSizeBytes = 0xffffu; // 65535
static_assert(kMaxRemoteLogFileMetadataSizeBytes <= 0xFFFFFFu,
"Only 24 bits available for encoding the metadata's length.");
// TODO(crbug.com/775415): Change back to (1u << 29) after resolving the issue
// where we read the entire file into memory.
const size_t kMaxRemoteLogFileSizeBytes = 50000000u;
namespace {
const base::TimeDelta kDefaultProactivePruningDelta =
base::TimeDelta::FromMinutes(5);
// Purge from local disk a log file which could not be properly started
// (e.g. error encountered when attempting to write the log header).
void DiscardLogFile(base::File* file, const base::FilePath& file_path) {
file->Close();
if (!base::DeleteFile(file_path, /*recursive=*/false)) {
LOG(ERROR) << "Failed to delete " << file_path << ".";
}
}
bool AreLogParametersValid(size_t max_file_size_bytes,
const std::string& metadata,
std::string* error_message) {
if (max_file_size_bytes == kWebRtcEventLogManagerUnlimitedFileSize) {
LOG(WARNING) << "Unlimited file sizes not allowed for remote-bound logs.";
*error_message = kStartRemoteLoggingFailureUnlimitedSizeDisallowed;
return false;
}
if (max_file_size_bytes > kMaxRemoteLogFileSizeBytes) {
LOG(WARNING) << "File size exceeds maximum allowed.";
*error_message = kStartRemoteLoggingFailureMaxSizeTooLarge;
return false;
}
if (metadata.length() > kMaxRemoteLogFileMetadataSizeBytes) {
LOG(ERROR) << "Excessively long metadata.";
*error_message = kStartRemoteLoggingFailureMetadaTooLong;
return false;
}
if (metadata.size() + kRemoteBoundLogFileHeaderSizeBytes >=
max_file_size_bytes) {
LOG(ERROR) << "Max file size and metadata must leave room for event log.";
*error_message = kStartRemoteLoggingFailureMaxSizeTooSmall;
return false;
}
return true;
}
base::Optional<base::TimeDelta> GetProactivePruningDelta() {
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
::switches::kWebRtcRemoteEventLogProactivePruningDelta)) {
const std::string delta_seconds_str =
base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
::switches::kWebRtcRemoteEventLogProactivePruningDelta);
int64_t seconds;
if (base::StringToInt64(delta_seconds_str, &seconds) && seconds >= 0) {
// A delta of 0 seconds is used to signal the intention of disabling
// proactive pruning altogether. (From the command line. Past the command
// line, we use an unset optional to signal that.)
return (seconds == 0) ? base::Optional<base::TimeDelta>()
: base::TimeDelta::FromSeconds(seconds);
} else {
LOG(WARNING) << "Proactive pruning delta could not be parsed.";
}
}
return kDefaultProactivePruningDelta;
}
} // namespace
const size_t kMaxActiveRemoteBoundWebRtcEventLogs = 3;
const size_t kMaxPendingRemoteBoundWebRtcEventLogs = 5;
static_assert(kMaxActiveRemoteBoundWebRtcEventLogs <=
kMaxPendingRemoteBoundWebRtcEventLogs,
"This assumption affects unit test coverage.");
const base::TimeDelta kRemoteBoundWebRtcEventLogsMaxRetention =
base::TimeDelta::FromDays(3);
const base::FilePath::CharType kRemoteBoundLogExtension[] =
FILE_PATH_LITERAL("log");
const uint8_t kRemoteBoundWebRtcEventLogFileVersion = 0;
const size_t kRemoteBoundLogFileHeaderSizeBytes = sizeof(uint32_t);
WebRtcRemoteEventLogManager::WebRtcRemoteEventLogManager(
WebRtcRemoteEventLogsObserver* observer)
: upload_suppression_disabled_(
base::CommandLine::ForCurrentProcess()->HasSwitch(
::switches::kWebRtcRemoteEventLogUploadNoSuppression)),
proactive_prune_scheduling_delta_(GetProactivePruningDelta()),
proactive_prune_scheduling_started_(false),
observer_(observer) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DETACH_FROM_SEQUENCE(io_task_sequence_checker_);
// Proactive pruning would not do anything at the moment; it will be started
// with the first enabled browser context. This will all have the benefit
// of doing so on io_task_sequence_checker_ rather than the UI thread.
}
WebRtcRemoteEventLogManager::~WebRtcRemoteEventLogManager() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
// TODO(crbug.com/775415): Purge from disk files which were being uploaded
// while destruction took place, thereby avoiding endless attempts to upload
// the same file.
}
void WebRtcRemoteEventLogManager::SetUrlRequestContextGetter(
net::URLRequestContextGetter* context_getter) {
DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
DCHECK(!uploader_factory_);
uploader_factory_ =
std::make_unique<WebRtcEventLogUploaderImpl::Factory>(context_getter);
}
void WebRtcRemoteEventLogManager::EnableForBrowserContext(
BrowserContextId browser_context_id,
const base::FilePath& browser_context_dir) {
DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
DCHECK(uploader_factory_) << "SetUrlRequestContextGetter() not called.";
DCHECK(!BrowserContextEnabled(browser_context_id)) << "Already enabled.";
const base::FilePath remote_bound_logs_dir =
GetRemoteBoundWebRtcEventLogsDir(browser_context_dir);
if (!MaybeCreateLogsDirectory(remote_bound_logs_dir)) {
LOG(WARNING)
<< "WebRtcRemoteEventLogManager couldn't create logs directory.";
return;
}
AddPendingLogs(browser_context_id, remote_bound_logs_dir);
enabled_browser_contexts_.insert(browser_context_id);
if (proactive_prune_scheduling_delta_.has_value() &&
!proactive_prune_scheduling_started_) {
proactive_prune_scheduling_started_ = true;
RecurringPendingLogsPrune();
}
}
// TODO(crbug.com/775415): Add unit tests.
void WebRtcRemoteEventLogManager::DisableForBrowserContext(
BrowserContextId browser_context_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
if (!BrowserContextEnabled(browser_context_id)) {
return; // Enabling may have failed due to lacking permissions.
}
enabled_browser_contexts_.erase(browser_context_id);
#if DCHECK_IS_ON()
// All of the RPHs associated with this BrowserContext must already have
// exited, which should have implicitly stopped all active logs.
auto pred = [browser_context_id](decltype(active_logs_)::value_type& log) {
return log.first.browser_context_id == browser_context_id;
};
DCHECK(std::count_if(active_logs_.begin(), active_logs_.end(), pred) == 0u);
#endif
// Pending logs for this BrowserContext are no longer eligible for upload.
// (Active uploads, if any, are not affected.)
for (auto it = pending_logs_.begin(); it != pending_logs_.end();) {
if (it->browser_context_id == browser_context_id) {
it = pending_logs_.erase(it);
} else {
++it;
}
}
}
bool WebRtcRemoteEventLogManager::PeerConnectionAdded(
const PeerConnectionKey& key,
const std::string& peer_connection_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
PrunePendingLogs(); // Infrequent event - good opportunity to prune.
const auto result = active_peer_connections_.emplace(key, peer_connection_id);
return result.second;
}
bool WebRtcRemoteEventLogManager::PeerConnectionRemoved(
const PeerConnectionKey& key) {
DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
PrunePendingLogs(); // Infrequent event - good opportunity to prune.
const auto peer_connection = active_peer_connections_.find(key);
if (peer_connection == active_peer_connections_.end()) {
return false;
}
MaybeStopRemoteLogging(key);
active_peer_connections_.erase(peer_connection);
MaybeStartUploading();
return true;
}
bool WebRtcRemoteEventLogManager::StartRemoteLogging(
int render_process_id,
BrowserContextId browser_context_id,
const std::string& peer_connection_id,
const base::FilePath& browser_context_dir,
size_t max_file_size_bytes,
const std::string& metadata,
std::string* error_message) {
DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
DCHECK(error_message);
DCHECK(error_message->empty());
if (!AreLogParametersValid(max_file_size_bytes, metadata, error_message)) {
// |error_message| will have been set by AreLogParametersValid().
DCHECK(!error_message->empty()) << "AreLogParametersValid() reported an "
"error without an error message.";
return false;
}
if (!BrowserContextEnabled(browser_context_id)) {
*error_message = kStartRemoteLoggingFailureGeneric;
return false;
}
PeerConnectionKey key;
if (!FindPeerConnection(render_process_id, peer_connection_id, &key)) {
*error_message = kStartRemoteLoggingFailureUnknownOrInactivePeerConnection;
return false;
}
// May not restart active remote logs.
auto it = active_logs_.find(key);
if (it != active_logs_.end()) {
LOG(ERROR) << "Remote logging already underway for ("
<< key.render_process_id << ", " << key.lid << ").";
*error_message = kStartRemoteLoggingFailureAlreadyLogging;
return false;
}
// This is a good opportunity to prune the list of pending logs, potentially
// making room for this file.
PrunePendingLogs();
if (!AdditionalActiveLogAllowed(key.browser_context_id)) {
// Intentionally use a generic error, so as to not leak information such
// as this being an incognito session (rejected elsewhere with the same
// error), or there being too many other peer connections on other tabs
// that might also be logging.
*error_message = kStartRemoteLoggingFailureGeneric;
return false;
}
return StartWritingLog(key, browser_context_dir, max_file_size_bytes,
metadata, error_message);
}
bool WebRtcRemoteEventLogManager::EventLogWrite(const PeerConnectionKey& key,
const std::string& message) {
DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
auto it = active_logs_.find(key);
if (it == active_logs_.end()) {
return false;
}
return WriteToLogFile(it, message);
}
void WebRtcRemoteEventLogManager::ClearCacheForBrowserContext(
BrowserContextId browser_context_id,
const base::Time& delete_begin,
const base::Time& delete_end) {
DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
MaybeRemovePendingLogs(delete_begin, delete_end, browser_context_id);
MaybeCancelUpload(delete_begin, delete_end, browser_context_id);
}
void WebRtcRemoteEventLogManager::RenderProcessHostExitedDestroyed(
int render_process_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
// Closing files will call MaybeStartUploading(). Avoid letting that upload
// any recently expired files.
PrunePendingLogs();
// Remove all of the peer connections associated with this render process.
// It's important to do this before closing the actual files, because closing
// files can trigger a new upload if no active peer connections are present.
auto pc_it = active_peer_connections_.begin();
while (pc_it != active_peer_connections_.end()) {
if (pc_it->first.render_process_id == render_process_id) {
pc_it = active_peer_connections_.erase(pc_it);
} else {
++pc_it;
}
}
// Close all of the files that were associated with peer connections which
// belonged to this render process.
auto log_it = active_logs_.begin();
while (log_it != active_logs_.end()) {
if (log_it->first.render_process_id == render_process_id) {
log_it = CloseLogFile(log_it);
} else {
++log_it;
}
}
// Though CloseLogFile() calls this, it's important to also do this
// explicitly, since it could be that no files were closed, but some
// active PeerConnections that were suppressing uploading are now gone.
MaybeStartUploading();
}
void WebRtcRemoteEventLogManager::OnWebRtcEventLogUploadComplete(
const base::FilePath& file_path,
bool upload_successful) {
DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
// Post a task to deallocate the uploader (can't do this directly,
// because this function is a callback from the uploader), potentially
// starting a new upload for the next file.
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(
&WebRtcRemoteEventLogManager::OnWebRtcEventLogUploadCompleteInternal,
base::Unretained(this)));
}
void WebRtcRemoteEventLogManager::SetWebRtcEventLogUploaderFactoryForTesting(
std::unique_ptr<WebRtcEventLogUploader::Factory> uploader_factory) {
DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
uploader_factory_ = std::move(uploader_factory);
}
bool WebRtcRemoteEventLogManager::BrowserContextEnabled(
BrowserContextId browser_context_id) const {
const auto it = enabled_browser_contexts_.find(browser_context_id);
return it != enabled_browser_contexts_.cend();
}
WebRtcRemoteEventLogManager::LogFilesMap::iterator
WebRtcRemoteEventLogManager::CloseLogFile(LogFilesMap::iterator it) {
DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
const PeerConnectionKey peer_connection = it->first;
it->second.file.Flush();
it = active_logs_.erase(it); // file.Close() called by destructor.
if (observer_) {
observer_->OnRemoteLogStopped(peer_connection);
}
MaybeStartUploading();
return it;
}
bool WebRtcRemoteEventLogManager::MaybeCreateLogsDirectory(
const base::FilePath& remote_bound_logs_dir) {
DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
if (base::PathExists(remote_bound_logs_dir)) {
if (!base::DirectoryExists(remote_bound_logs_dir)) {
LOG(ERROR) << "Path for remote-bound logs is taken by a non-directory.";
return false;
}
} else if (!base::CreateDirectory(remote_bound_logs_dir)) {
LOG(ERROR) << "Failed to create the local directory for remote-bound logs.";
return false;
}
// TODO(crbug.com/775415): Test for appropriate permissions.
return true;
}
void WebRtcRemoteEventLogManager::AddPendingLogs(
BrowserContextId browser_context_id,
const base::FilePath& remote_bound_logs_dir) {
DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
base::FilePath::StringType pattern =
base::FilePath::StringType(FILE_PATH_LITERAL("*")) +
base::FilePath::kExtensionSeparator + kRemoteBoundLogExtension;
base::FileEnumerator enumerator(remote_bound_logs_dir,
/*recursive=*/false,
base::FileEnumerator::FILES, pattern);
for (auto path = enumerator.Next(); !path.empty(); path = enumerator.Next()) {
const auto last_modified = enumerator.GetInfo().GetLastModifiedTime();
auto it = pending_logs_.emplace(browser_context_id, path, last_modified);
DCHECK(it.second); // No pre-existing entry.
}
MaybeStartUploading();
}
bool WebRtcRemoteEventLogManager::StartWritingLog(
const PeerConnectionKey& key,
const base::FilePath& browser_context_dir,
size_t max_file_size_bytes,
const std::string& metadata,
std::string* error_message) {
DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
// WriteAtCurrentPos() only allows writing up to max-int at a time. We could
// iterate to do more, but we don't expect to ever need to, so it's easier
// to disallow it.
if (metadata.length() >
static_cast<size_t>(std::numeric_limits<int>::max())) {
LOG(WARNING) << "Metadata too long to be written in one go.";
*error_message = kStartRemoteLoggingFailureMetadaTooLong;
return false;
}
// Randomize a new filename. In the highly unlikely event that this filename
// is already taken, it will be treated the same way as any other failure
// to start the log file.
// TODO(crbug.com/775415): Add a unit test for above comment.
const std::string unique_filename =
"event_log_" + std::to_string(base::RandUint64());
const base::FilePath base_path =
GetRemoteBoundWebRtcEventLogsDir(browser_context_dir);
const base::FilePath file_path = base_path.AppendASCII(unique_filename)
.AddExtension(kRemoteBoundLogExtension);
// Attempt to create the file.
constexpr int file_flags = base::File::FLAG_CREATE | base::File::FLAG_WRITE |
base::File::FLAG_EXCLUSIVE_WRITE;
base::File file(file_path, file_flags);
if (!file.IsValid() || !file.created()) {
LOG(WARNING) << "Couldn't create and/or open remote WebRTC event log file.";
// Intentionally using a generic error; look for other places where it's
// set for an explanation why.
*error_message = kStartRemoteLoggingFailureGeneric;
return false;
}
const uint32_t header_host_order =
static_cast<uint32_t>(metadata.length()) |
(kRemoteBoundWebRtcEventLogFileVersion << 24);
static_assert(kRemoteBoundLogFileHeaderSizeBytes == sizeof(uint32_t),
"Restructure this otherwise.");
char header[sizeof(uint32_t)];
base::WriteBigEndian<uint32_t>(header, header_host_order);
int written = file.WriteAtCurrentPos(header, sizeof(header));
if (written != arraysize(header)) {
LOG(WARNING) << "Failed to write header to log file.";
DiscardLogFile(&file, file_path);
// Intentionally using a generic error; look for other places where it's
// set for an explanation why.
*error_message = kStartRemoteLoggingFailureGeneric;
return false;
}
const int metadata_length = static_cast<int>(metadata.length());
written = file.WriteAtCurrentPos(metadata.c_str(), metadata_length);
if (written != metadata_length) {
LOG(WARNING) << "Failed to write metadata to log file.";
DiscardLogFile(&file, file_path);
// Intentionally using a generic error; look for other places where it's
// set for an explanation why.
*error_message = kStartRemoteLoggingFailureGeneric;
return false;
}
// Record that we're now writing this remote-bound log to this file.
const size_t header_and_metadata_size_bytes =
kRemoteBoundLogFileHeaderSizeBytes + metadata_length;
const auto it = active_logs_.emplace(
key, LogFile(file_path, std::move(file), max_file_size_bytes,
header_and_metadata_size_bytes));
DCHECK(it.second);
observer_->OnRemoteLogStarted(key, file_path);
return true;
}
void WebRtcRemoteEventLogManager::MaybeStopRemoteLogging(
const PeerConnectionKey& key) {
DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
const auto it = active_logs_.find(key);
if (it == active_logs_.end()) {
return;
}
it->second.file.Flush();
it->second.file.Close();
// The current time is a good enough approximation of the file's last
// modification time.
const base::Time last_modified = base::Time::Now();
// The stopped log becomes a pending log. It is no longer an active log.
const auto emplace_result = pending_logs_.emplace(
key.browser_context_id, it->second.path, last_modified);
DCHECK(emplace_result.second); // No pre-existing entry.
active_logs_.erase(it);
observer_->OnRemoteLogStopped(key);
MaybeStartUploading();
}
void WebRtcRemoteEventLogManager::PrunePendingLogs() {
DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
MaybeRemovePendingLogs(
base::Time::Min(),
base::Time::Now() - kRemoteBoundWebRtcEventLogsMaxRetention);
}
void WebRtcRemoteEventLogManager::RecurringPendingLogsPrune() {
DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
DCHECK(proactive_prune_scheduling_delta_.has_value());
DCHECK_GT(*proactive_prune_scheduling_delta_, base::TimeDelta());
DCHECK(proactive_prune_scheduling_started_);
PrunePendingLogs();
base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&WebRtcRemoteEventLogManager::RecurringPendingLogsPrune,
base::Unretained(this)),
*proactive_prune_scheduling_delta_);
}
void WebRtcRemoteEventLogManager::MaybeRemovePendingLogs(
const base::Time& delete_begin,
const base::Time& delete_end,
base::Optional<BrowserContextId> browser_context_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
for (auto it = pending_logs_.begin(); it != pending_logs_.end();) {
if (LogFileMatchesFilter(*it, delete_begin, delete_end,
browser_context_id)) {
DVLOG(1) << "Removing " << it->path << ".";
if (!base::DeleteFile(it->path, /*recursive=*/false)) {
LOG(ERROR) << "Failed to delete " << it->path << ".";
}
it = pending_logs_.erase(it);
} else {
DVLOG(1) << "Keeping " << it->path << " on disk.";
++it;
}
}
}
void WebRtcRemoteEventLogManager::MaybeCancelUpload(
const base::Time& delete_begin,
const base::Time& delete_end,
base::Optional<BrowserContextId> browser_context_id) {
if (uploader_) {
const WebRtcLogFileInfo& info = uploader_->GetWebRtcLogFileInfo();
if (LogFileMatchesFilter(info, delete_begin, delete_end,
browser_context_id)) {
// Cancel the upload. (If the upload has asynchronously completed by now,
// the uploader must have posted a task back to our queue to delete it
// and move on to the next file; cancellation is reported as unsucessful
// in that case.)
const bool cancelled = uploader_->Cancel();
if (cancelled) {
uploader_.reset();
MaybeStartUploading();
}
}
}
}
bool WebRtcRemoteEventLogManager::LogFileMatchesFilter(
const WebRtcLogFileInfo& log,
const base::Time& range_begin,
const base::Time& range_end,
base::Optional<BrowserContextId> browser_context_id) const {
if (browser_context_id && *browser_context_id != log.browser_context_id) {
return false;
}
return (range_begin.is_null() || range_begin <= log.last_modified) &&
(range_end.is_null() || log.last_modified < range_end);
}
bool WebRtcRemoteEventLogManager::AdditionalActiveLogAllowed(
BrowserContextId browser_context_id) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
// Limit over concurrently active logs (across BrowserContext-s).
if (active_logs_.size() >= kMaxActiveRemoteBoundWebRtcEventLogs) {
return false;
}
// Limit over the number of pending logs (per BrowserContext). We count active
// logs too, since they become pending logs once completed.
const size_t active_count = std::count_if(
active_logs_.begin(), active_logs_.end(),
[browser_context_id](const decltype(active_logs_)::value_type& log) {
return log.first.browser_context_id == browser_context_id;
});
const size_t pending_count = std::count_if(
pending_logs_.begin(), pending_logs_.end(),
[browser_context_id](const decltype(pending_logs_)::value_type& log) {
return log.browser_context_id == browser_context_id;
});
return active_count + pending_count < kMaxPendingRemoteBoundWebRtcEventLogs;
}
bool WebRtcRemoteEventLogManager::UploadingAllowed() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
return upload_suppression_disabled_ || active_peer_connections_.empty();
}
void WebRtcRemoteEventLogManager::MaybeStartUploading() {
DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
PrunePendingLogs(); // Avoid uploading freshly expired files.
if (uploader_) {
return; // Upload already underway.
}
if (pending_logs_.empty()) {
return; // Nothing to upload.
}
if (!UploadingAllowed()) {
return;
}
// The uploader takes ownership of the file; it's no longer considered to be
// pending. (If the upload fails, the log will be deleted.)
// TODO(crbug.com/775415): Add more refined retry behavior, so that we would
// not delete the log permanently if the network is just down, on the one
// hand, but also would not be uploading unlimited data on endless retries on
// the other hand.
// TODO(crbug.com/814362): Delay the upload's start.
// TODO(crbug.com/775415): Rename the file before uploading, so that we would
// not retry the upload after restarting Chrome, if the upload is interrupted.
uploader_ = uploader_factory_->Create(*pending_logs_.begin(), this);
pending_logs_.erase(pending_logs_.begin());
}
void WebRtcRemoteEventLogManager::OnWebRtcEventLogUploadCompleteInternal() {
DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
uploader_.reset();
MaybeStartUploading();
}
bool WebRtcRemoteEventLogManager::FindPeerConnection(
int render_process_id,
const std::string& peer_connection_id,
PeerConnectionKey* key) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
const auto it = FindNextPeerConnection(active_peer_connections_.cbegin(),
render_process_id, peer_connection_id);
if (it == active_peer_connections_.cend()) {
return false;
}
// Make sure that the peer connection ID is unique for the renderer process
// in which it exists, though not necessarily between renderer processes.
// (The helper exists just to allow this DCHECK.)
DCHECK(FindNextPeerConnection(std::next(it), render_process_id,
peer_connection_id) ==
active_peer_connections_.cend());
*key = it->first;
return true;
}
std::map<WebRtcEventLogPeerConnectionKey, const std::string>::const_iterator
WebRtcRemoteEventLogManager::FindNextPeerConnection(
std::map<PeerConnectionKey, const std::string>::const_iterator begin,
int render_process_id,
const std::string& peer_connection_id) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
const auto end = active_peer_connections_.cend();
for (auto it = begin; it != end; it++) {
if (it->first.render_process_id == render_process_id &&
it->second == peer_connection_id) {
return it;
}
}
return end;
}