blob: 6922f9f3c22d5c401585142466df3b0815d76d3f [file] [log] [blame]
// Copyright (c) 2017 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 "content/browser/webrtc/webrtc_event_log_manager.h"
#include "base/task_scheduler/post_task.h"
#include "content/common/media/peer_connection_tracker_messages.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_process_host.h"
namespace content {
namespace {
using BrowserContextId = WebRtcEventLogManager::BrowserContextId;
const BrowserContextId kNullBrowserContextId =
reinterpret_cast<BrowserContextId>(nullptr);
class PeerConnectionTrackerProxyImpl
: public WebRtcEventLogManager::PeerConnectionTrackerProxy {
public:
~PeerConnectionTrackerProxyImpl() override = default;
void SetWebRtcEventLoggingState(const WebRtcEventLogPeerConnectionKey& key,
bool event_logging_enabled) override {
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::BindOnce(
&PeerConnectionTrackerProxyImpl::SetWebRtcEventLoggingStateInternal,
key, event_logging_enabled));
}
private:
static void SetWebRtcEventLoggingStateInternal(
WebRtcEventLogPeerConnectionKey key,
bool event_logging_enabled) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
RenderProcessHost* host = RenderProcessHost::FromID(key.render_process_id);
if (!host) {
return; // The host has been asynchronously removed; not a problem.
}
if (event_logging_enabled) {
host->Send(new PeerConnectionTracker_StartEventLogOutput(key.lid));
} else {
host->Send(new PeerConnectionTracker_StopEventLog(key.lid));
}
}
};
const BrowserContext* GetBrowserContext(int render_process_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
RenderProcessHost* const host = RenderProcessHost::FromID(render_process_id);
return host ? host->GetBrowserContext() : nullptr;
}
} // namespace
const size_t kWebRtcEventLogManagerUnlimitedFileSize = 0;
WebRtcEventLogManager* WebRtcEventLogManager::g_webrtc_event_log_manager =
nullptr;
BrowserContextId WebRtcEventLogManager::GetBrowserContextId(
const BrowserContext* browser_context) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
return reinterpret_cast<BrowserContextId>(browser_context);
}
BrowserContextId WebRtcEventLogManager::GetBrowserContextId(
int render_process_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
const BrowserContext* browser_context = GetBrowserContext(render_process_id);
return GetBrowserContextId(browser_context);
}
WebRtcEventLogManager* WebRtcEventLogManager::CreateSingletonInstance() {
DCHECK(!g_webrtc_event_log_manager);
g_webrtc_event_log_manager = new WebRtcEventLogManager;
return g_webrtc_event_log_manager;
}
WebRtcEventLogManager* WebRtcEventLogManager::GetInstance() {
return g_webrtc_event_log_manager;
}
WebRtcEventLogManager::WebRtcEventLogManager()
: WebRtcEventLogManager(base::CreateSequencedTaskRunnerWithTraits(
{base::MayBlock(), base::TaskPriority::BACKGROUND,
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})) {}
WebRtcEventLogManager::WebRtcEventLogManager(
const scoped_refptr<base::SequencedTaskRunner>& task_runner)
: local_logs_observer_(nullptr),
remote_logs_observer_(nullptr),
local_logs_manager_(this),
remote_logs_manager_(this),
pc_tracker_proxy_(new PeerConnectionTrackerProxyImpl),
task_runner_(task_runner) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(!g_webrtc_event_log_manager);
g_webrtc_event_log_manager = this;
}
WebRtcEventLogManager::~WebRtcEventLogManager() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
for (RenderProcessHost* host : observed_render_process_hosts_) {
host->RemoveObserver(this);
}
DCHECK(g_webrtc_event_log_manager);
g_webrtc_event_log_manager = nullptr;
}
void WebRtcEventLogManager::EnableForBrowserContext(
const BrowserContext* browser_context,
base::OnceClosure reply) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(browser_context);
CHECK(!browser_context->IsOffTheRecord());
// The object outlives the task queue - base::Unretained(this) is safe.
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&WebRtcEventLogManager::EnableForBrowserContextInternal,
base::Unretained(this),
GetBrowserContextId(browser_context),
browser_context->GetPath(), std::move(reply)));
}
void WebRtcEventLogManager::DisableForBrowserContext(
BrowserContextId browser_context_id,
base::OnceClosure reply) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK_NE(browser_context_id, kNullBrowserContextId);
// The object outlives the task queue - base::Unretained(this) is safe.
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&WebRtcEventLogManager::DisableForBrowserContextInternal,
base::Unretained(this), browser_context_id,
std::move(reply)));
}
void WebRtcEventLogManager::PeerConnectionAdded(
int render_process_id,
int lid,
base::OnceCallback<void(bool)> reply) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
RenderProcessHost* rph = RenderProcessHost::FromID(render_process_id);
if (!rph) {
// RPH died before processing of this notification.
MaybeReply(std::move(reply), false);
return;
}
auto it = observed_render_process_hosts_.find(rph);
if (it == observed_render_process_hosts_.end()) {
// This is the first PeerConnection which we see that's associated
// with this RPH.
rph->AddObserver(this);
observed_render_process_hosts_.insert(rph);
}
const auto browser_context_id = GetBrowserContextId(rph->GetBrowserContext());
DCHECK_NE(browser_context_id, kNullBrowserContextId);
// The object outlives the task queue - base::Unretained(this) is safe.
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&WebRtcEventLogManager::PeerConnectionAddedInternal,
base::Unretained(this),
PeerConnectionKey(render_process_id, lid, browser_context_id),
std::move(reply)));
}
void WebRtcEventLogManager::PeerConnectionRemoved(
int render_process_id,
int lid,
base::OnceCallback<void(bool)> reply) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
const auto browser_context_id = GetBrowserContextId(render_process_id);
if (browser_context_id == kNullBrowserContextId) {
// RPH died before processing of this notification. This is handled by
// RenderProcessExited() / RenderProcessHostDestroyed.
MaybeReply(std::move(reply), false);
return;
}
// The object outlives the task queue - base::Unretained(this) is safe.
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&WebRtcEventLogManager::PeerConnectionRemovedInternal,
base::Unretained(this),
PeerConnectionKey(render_process_id, lid, browser_context_id),
std::move(reply)));
}
void WebRtcEventLogManager::PeerConnectionStopped(
int render_process_id,
int lid,
base::OnceCallback<void(bool)> reply) {
return PeerConnectionRemoved(render_process_id, lid, std::move(reply));
}
void WebRtcEventLogManager::EnableLocalLogging(
const base::FilePath& base_path,
size_t max_file_size_bytes,
base::OnceCallback<void(bool)> reply) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(!base_path.empty());
// The object outlives the task queue - base::Unretained(this) is safe.
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&WebRtcEventLogManager::EnableLocalLoggingInternal,
base::Unretained(this), base_path, max_file_size_bytes,
std::move(reply)));
}
void WebRtcEventLogManager::DisableLocalLogging(
base::OnceCallback<void(bool)> reply) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// The object outlives the task queue - base::Unretained(this) is safe.
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&WebRtcEventLogManager::DisableLocalLoggingInternal,
base::Unretained(this), std::move(reply)));
}
void WebRtcEventLogManager::StartRemoteLogging(
int render_process_id,
int lid,
size_t max_file_size_bytes,
const std::string& metadata,
base::OnceCallback<void(bool)> reply) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
const BrowserContext* browser_context = GetBrowserContext(render_process_id);
if (!browser_context || browser_context->IsOffTheRecord()) {
// RPH died before processing of this notification, or is incognito.
MaybeReply(std::move(reply), false);
return;
}
const auto browser_context_id = GetBrowserContextId(browser_context);
DCHECK_NE(browser_context_id, kNullBrowserContextId);
// The object outlives the task queue - base::Unretained(this) is safe.
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&WebRtcEventLogManager::StartRemoteLoggingInternal,
base::Unretained(this),
PeerConnectionKey(render_process_id, lid, browser_context_id),
browser_context->GetPath(), max_file_size_bytes, metadata,
std::move(reply)));
}
void WebRtcEventLogManager::OnWebRtcEventLogWrite(
int render_process_id,
int lid,
const std::string& message,
base::OnceCallback<void(std::pair<bool, bool>)> reply) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
const BrowserContext* browser_context = GetBrowserContext(render_process_id);
if (!browser_context) {
// RPH died before processing of this notification.
MaybeReply(std::move(reply), false, false);
return;
}
const auto browser_context_id = GetBrowserContextId(browser_context);
DCHECK_NE(browser_context_id, kNullBrowserContextId);
const bool remote_logging_allowed = !browser_context->IsOffTheRecord();
// The object outlives the task queue - base::Unretained(this) is safe.
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&WebRtcEventLogManager::OnWebRtcEventLogWriteInternal,
base::Unretained(this),
PeerConnectionKey(render_process_id, lid, browser_context_id),
remote_logging_allowed, message, std::move(reply)));
}
void WebRtcEventLogManager::SetLocalLogsObserver(
WebRtcLocalEventLogsObserver* observer,
base::OnceClosure reply) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// The object outlives the task queue - base::Unretained(this) is safe.
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&WebRtcEventLogManager::SetLocalLogsObserverInternal,
base::Unretained(this), observer, std::move(reply)));
}
void WebRtcEventLogManager::SetRemoteLogsObserver(
WebRtcRemoteEventLogsObserver* observer,
base::OnceClosure reply) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// The object outlives the task queue - base::Unretained(this) is safe.
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&WebRtcEventLogManager::SetRemoteLogsObserverInternal,
base::Unretained(this), observer, std::move(reply)));
}
scoped_refptr<base::SequencedTaskRunner>&
WebRtcEventLogManager::GetTaskRunnerForTesting() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
return task_runner_;
}
void WebRtcEventLogManager::RenderProcessExited(RenderProcessHost* host,
base::TerminationStatus status,
int exit_code) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
RenderProcessHostExitedDestroyed(host);
}
void WebRtcEventLogManager::RenderProcessHostDestroyed(
RenderProcessHost* host) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
RenderProcessHostExitedDestroyed(host);
}
void WebRtcEventLogManager::RenderProcessHostExitedDestroyed(
RenderProcessHost* host) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(host);
auto it = observed_render_process_hosts_.find(host);
if (it == observed_render_process_hosts_.end()) {
return; // We've never seen PeerConnections associated with this RPH.
}
host->RemoveObserver(this);
observed_render_process_hosts_.erase(host);
// The object outlives the task queue - base::Unretained(this) is safe.
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&WebRtcEventLogManager::RenderProcessExitedInternal,
base::Unretained(this), host->GetID()));
}
void WebRtcEventLogManager::OnLocalLogStarted(PeerConnectionKey peer_connection,
const base::FilePath& file_path) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
OnLoggingTargetStarted(LoggingTarget::kLocalLogging, peer_connection);
if (local_logs_observer_) {
local_logs_observer_->OnLocalLogStarted(peer_connection, file_path);
}
}
void WebRtcEventLogManager::OnLocalLogStopped(
PeerConnectionKey peer_connection) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
OnLoggingTargetStopped(LoggingTarget::kLocalLogging, peer_connection);
if (local_logs_observer_) {
local_logs_observer_->OnLocalLogStopped(peer_connection);
}
}
void WebRtcEventLogManager::OnRemoteLogStarted(
PeerConnectionKey key,
const base::FilePath& file_path) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
OnLoggingTargetStarted(LoggingTarget::kRemoteLogging, key);
if (remote_logs_observer_) {
remote_logs_observer_->OnRemoteLogStarted(key, file_path);
}
}
void WebRtcEventLogManager::OnRemoteLogStopped(
WebRtcEventLogPeerConnectionKey key) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
OnLoggingTargetStopped(LoggingTarget::kRemoteLogging, key);
if (remote_logs_observer_) {
remote_logs_observer_->OnRemoteLogStopped(key);
}
}
void WebRtcEventLogManager::OnLoggingTargetStarted(LoggingTarget target,
PeerConnectionKey key) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
auto it = peer_connections_with_event_logging_enabled_.find(key);
if (it != peer_connections_with_event_logging_enabled_.end()) {
DCHECK_EQ((it->second & target), 0u);
it->second |= target;
} else {
// This is the first client for WebRTC event logging - let WebRTC know
// that it should start informing us of events.
peer_connections_with_event_logging_enabled_.emplace(key, target);
pc_tracker_proxy_->SetWebRtcEventLoggingState(key, true);
}
}
void WebRtcEventLogManager::OnLoggingTargetStopped(LoggingTarget target,
PeerConnectionKey key) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
// Record that we're no longer performing this type of logging for this PC.
auto it = peer_connections_with_event_logging_enabled_.find(key);
CHECK(it != peer_connections_with_event_logging_enabled_.end());
DCHECK_NE(it->second, 0u);
it->second &= ~target;
// If we're not doing any other type of logging for this peer connection,
// it's time to stop receiving notifications for it from WebRTC.
if (it->second == 0u) {
peer_connections_with_event_logging_enabled_.erase(it);
pc_tracker_proxy_->SetWebRtcEventLoggingState(key, false);
}
}
void WebRtcEventLogManager::EnableForBrowserContextInternal(
BrowserContextId browser_context_id,
const base::FilePath& browser_context_dir,
base::OnceClosure reply) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
DCHECK_NE(browser_context_id, kNullBrowserContextId);
remote_logs_manager_.EnableForBrowserContext(browser_context_id,
browser_context_dir);
if (reply) {
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, std::move(reply));
}
}
void WebRtcEventLogManager::DisableForBrowserContextInternal(
BrowserContextId browser_context_id,
base::OnceClosure reply) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
remote_logs_manager_.DisableForBrowserContext(browser_context_id);
if (reply) {
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, std::move(reply));
}
}
void WebRtcEventLogManager::PeerConnectionAddedInternal(
PeerConnectionKey key,
base::OnceCallback<void(bool)> reply) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
const bool local_result = local_logs_manager_.PeerConnectionAdded(key);
const bool remote_result = remote_logs_manager_.PeerConnectionAdded(key);
DCHECK_EQ(local_result, remote_result);
if (reply) {
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::BindOnce(std::move(reply), local_result));
}
}
void WebRtcEventLogManager::PeerConnectionRemovedInternal(
PeerConnectionKey key,
base::OnceCallback<void(bool)> reply) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
const bool local_result = local_logs_manager_.PeerConnectionRemoved(key);
const bool remote_result = remote_logs_manager_.PeerConnectionRemoved(key);
DCHECK_EQ(local_result, remote_result);
if (reply) {
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::BindOnce(std::move(reply), local_result));
}
}
void WebRtcEventLogManager::EnableLocalLoggingInternal(
const base::FilePath& base_path,
size_t max_file_size_bytes,
base::OnceCallback<void(bool)> reply) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
const bool result =
local_logs_manager_.EnableLogging(base_path, max_file_size_bytes);
if (reply) {
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::BindOnce(std::move(reply), result));
}
}
void WebRtcEventLogManager::DisableLocalLoggingInternal(
base::OnceCallback<void(bool)> reply) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
const bool result = local_logs_manager_.DisableLogging();
if (reply) {
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::BindOnce(std::move(reply), result));
}
}
void WebRtcEventLogManager::StartRemoteLoggingInternal(
PeerConnectionKey key,
const base::FilePath& browser_context_dir,
size_t max_file_size_bytes,
const std::string metadata,
base::OnceCallback<void(bool)> reply) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
const bool result = remote_logs_manager_.StartRemoteLogging(
key, browser_context_dir, max_file_size_bytes, std::move(metadata));
if (reply) {
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::BindOnce(std::move(reply), result));
}
}
void WebRtcEventLogManager::OnWebRtcEventLogWriteInternal(
PeerConnectionKey key,
bool remote_logging_allowed,
const std::string message,
base::OnceCallback<void(std::pair<bool, bool>)> reply) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
const bool local_result =
local_logs_manager_.EventLogWrite(key, std::move(message));
const bool remote_result =
remote_logging_allowed
? remote_logs_manager_.EventLogWrite(key, std::move(message))
: false;
if (reply) {
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::BindOnce(std::move(reply),
std::make_pair(local_result, remote_result)));
}
}
void WebRtcEventLogManager::RenderProcessExitedInternal(int render_process_id) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
local_logs_manager_.RenderProcessHostExitedDestroyed(render_process_id);
remote_logs_manager_.RenderProcessHostExitedDestroyed(render_process_id);
}
void WebRtcEventLogManager::SetLocalLogsObserverInternal(
WebRtcLocalEventLogsObserver* observer,
base::OnceClosure reply) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
local_logs_observer_ = observer;
if (reply) {
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, std::move(reply));
}
}
void WebRtcEventLogManager::SetRemoteLogsObserverInternal(
WebRtcRemoteEventLogsObserver* observer,
base::OnceClosure reply) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
remote_logs_observer_ = observer;
if (reply) {
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, std::move(reply));
}
}
void WebRtcEventLogManager::MaybeReply(base::OnceClosure reply) {
if (!reply) {
return;
}
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, std::move(reply));
}
void WebRtcEventLogManager::MaybeReply(base::OnceCallback<void(bool)> reply,
bool value) {
if (!reply) {
return;
}
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::BindOnce(std::move(reply), value));
}
void WebRtcEventLogManager::MaybeReply(
base::OnceCallback<void(std::pair<bool, bool>)> reply,
bool first,
bool second) {
if (!reply) {
return;
}
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::BindOnce(std::move(reply), std::make_pair(first, second)));
}
void WebRtcEventLogManager::SetClockForTesting(base::Clock* clock,
base::OnceClosure reply) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
auto task = [](WebRtcEventLogManager* manager, base::Clock* clock,
base::OnceClosure reply) {
manager->local_logs_manager_.SetClockForTesting(clock);
if (reply) {
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, std::move(reply));
}
};
// The object outlives the task queue - base::Unretained(this) is safe.
task_runner_->PostTask(FROM_HERE, base::BindOnce(task, base::Unretained(this),
clock, std::move(reply)));
}
void WebRtcEventLogManager::SetPeerConnectionTrackerProxyForTesting(
std::unique_ptr<PeerConnectionTrackerProxy> pc_tracker_proxy,
base::OnceClosure reply) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
auto task = [](WebRtcEventLogManager* manager,
std::unique_ptr<PeerConnectionTrackerProxy> pc_tracker_proxy,
base::OnceClosure reply) {
manager->pc_tracker_proxy_ = std::move(pc_tracker_proxy);
if (reply) {
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, std::move(reply));
}
};
// The object outlives the task queue - base::Unretained(this) is safe.
task_runner_->PostTask(
FROM_HERE, base::BindOnce(task, base::Unretained(this),
std::move(pc_tracker_proxy), std::move(reply)));
}
void WebRtcEventLogManager::SetWebRtcEventLogUploaderFactoryForTesting(
std::unique_ptr<WebRtcEventLogUploader::Factory> uploader_factory,
base::OnceClosure reply) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
auto task =
[](WebRtcEventLogManager* manager,
std::unique_ptr<WebRtcEventLogUploader::Factory> uploader_factory,
base::OnceClosure reply) {
auto& remote_logs_manager = manager->remote_logs_manager_;
remote_logs_manager.SetWebRtcEventLogUploaderFactoryForTesting(
std::move(uploader_factory));
if (reply) {
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
std::move(reply));
}
};
// The object outlives the task queue - base::Unretained(this) is safe.
task_runner_->PostTask(
FROM_HERE, base::BindOnce(task, base::Unretained(this),
std::move(uploader_factory), std::move(reply)));
}
} // namespace content