blob: 07744e2dc70a2b77e0a70012d22134a2d5411cc9 [file] [log] [blame]
// Copyright 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 "chrome/browser/media/webrtc/webrtc_event_log_manager.h"
#include <limits>
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/memory/ptr_util.h"
#include "base/task/thread_pool.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/pref_names.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/network_service_instance.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
namespace webrtc_event_logging {
namespace {
using content::BrowserContext;
using content::BrowserThread;
using content::RenderFrameHost;
using content::RenderProcessHost;
using BrowserContextId = WebRtcEventLogManager::BrowserContextId;
class PeerConnectionTrackerProxyImpl
: public WebRtcEventLogManager::PeerConnectionTrackerProxy {
public:
~PeerConnectionTrackerProxyImpl() override = default;
void EnableWebRtcEventLogging(const WebRtcEventLogPeerConnectionKey& key,
int output_period_ms) override {
content::GetUIThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(
&PeerConnectionTrackerProxyImpl::EnableWebRtcEventLoggingInternal,
key, output_period_ms));
}
void DisableWebRtcEventLogging(
const WebRtcEventLogPeerConnectionKey& key) override {
content::GetUIThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(
&PeerConnectionTrackerProxyImpl::DisableWebRtcEventLoggingInternal,
key));
}
private:
static void EnableWebRtcEventLoggingInternal(
WebRtcEventLogPeerConnectionKey key,
int output_period_ms) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
auto* host =
RenderFrameHost::FromID(key.render_process_id, key.render_frame_id);
if (!host) {
return; // The host has been asynchronously removed; not a problem.
}
host->EnableWebRtcEventLogOutput(key.lid, output_period_ms);
}
static void DisableWebRtcEventLoggingInternal(
WebRtcEventLogPeerConnectionKey key) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
auto* host =
RenderFrameHost::FromID(key.render_process_id, key.render_frame_id);
if (!host) {
return; // The host has been asynchronously removed; not a problem.
}
host->DisableWebRtcEventLogOutput(key.lid);
}
};
// Check whether remote-bound logging is generally allowed, although not
// necessarily for any given user profile.
// 1. Certain platforms (mobile) are blocked from remote-bound logging.
// 2. There is a Finch-controlled kill-switch for the feature.
bool IsRemoteLoggingFeatureEnabled() {
#if BUILDFLAG(IS_ANDROID)
bool enabled = false;
#else
bool enabled = base::FeatureList::IsEnabled(features::kWebRtcRemoteEventLog);
#endif
VLOG(1) << "WebRTC remote-bound event logging "
<< (enabled ? "enabled" : "disabled") << ".";
return enabled;
}
BrowserContext* GetBrowserContext(int render_process_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
RenderProcessHost* const host = RenderProcessHost::FromID(render_process_id);
return host ? host->GetBrowserContext() : nullptr;
}
// Post reply back if non-empty.
template <typename... Args>
inline void MaybeReply(const base::Location& location,
base::OnceCallback<void(Args...)> reply,
Args... args) {
if (reply) {
content::GetUIThreadTaskRunner({})->PostTask(
location, base::BindOnce(std::move(reply), args...));
}
}
} // namespace
WebRtcEventLogManager* WebRtcEventLogManager::g_webrtc_event_log_manager =
nullptr;
std::unique_ptr<WebRtcEventLogManager>
WebRtcEventLogManager::CreateSingletonInstance() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(!g_webrtc_event_log_manager);
g_webrtc_event_log_manager = new WebRtcEventLogManager;
return base::WrapUnique<WebRtcEventLogManager>(g_webrtc_event_log_manager);
}
WebRtcEventLogManager* WebRtcEventLogManager::GetInstance() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
return g_webrtc_event_log_manager;
}
base::FilePath WebRtcEventLogManager::GetRemoteBoundWebRtcEventLogsDir(
content::BrowserContext* browser_context) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(browser_context);
// Incognito BrowserContext will return their parent profile's directory.
return webrtc_event_logging::GetRemoteBoundWebRtcEventLogsDir(
browser_context->GetPath());
}
WebRtcEventLogManager::WebRtcEventLogManager()
: task_runner_(base::ThreadPool::CreateUpdateableSequencedTaskRunner(
{base::MayBlock(), base::TaskPriority::BEST_EFFORT,
base::ThreadPolicy::PREFER_BACKGROUND,
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})),
num_user_blocking_tasks_(0),
remote_logging_feature_enabled_(IsRemoteLoggingFeatureEnabled()),
local_logs_observer_(nullptr),
remote_logs_observer_(nullptr),
local_logs_manager_(this),
remote_logs_manager_(this, task_runner_),
pc_tracker_proxy_(new PeerConnectionTrackerProxyImpl),
first_browser_context_initializations_done_(false) {
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(
BrowserContext* browser_context,
base::OnceClosure reply) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(browser_context);
CHECK(!browser_context->IsOffTheRecord());
if (!first_browser_context_initializations_done_) {
OnFirstBrowserContextLoaded();
first_browser_context_initializations_done_ = true;
}
StartListeningForPrefChangeForBrowserContext(browser_context);
if (!IsRemoteLoggingAllowedForBrowserContext(browser_context)) {
// If remote-bound logging was enabled during a previous Chrome session,
// it might have produced some pending log files, which we will now
// wish to remove.
// |this| is destroyed by ~BrowserProcessImpl(), so base::Unretained(this)
// will not be dereferenced after destruction.
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&WebRtcEventLogManager::
RemovePendingRemoteBoundLogsForNotEnabledBrowserContext,
base::Unretained(this), GetBrowserContextId(browser_context),
browser_context->GetPath(), std::move(reply)));
return;
}
// |this| is destroyed by ~BrowserProcessImpl(), so base::Unretained(this)
// will not be dereferenced after destruction.
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&WebRtcEventLogManager::EnableRemoteBoundLoggingForBrowserContext,
base::Unretained(this), GetBrowserContextId(browser_context),
browser_context->GetPath(), std::move(reply)));
}
void WebRtcEventLogManager::DisableForBrowserContext(
content::BrowserContext* browser_context,
base::OnceClosure reply) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(browser_context);
StopListeningForPrefChangeForBrowserContext(browser_context);
// |this| is destroyed by ~BrowserProcessImpl(), so base::Unretained(this)
// will not be dereferenced after destruction.
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&WebRtcEventLogManager::DisableRemoteBoundLoggingForBrowserContext,
base::Unretained(this), GetBrowserContextId(browser_context),
std::move(reply)));
}
void WebRtcEventLogManager::OnPeerConnectionAdded(
content::GlobalRenderFrameHostId frame_id,
int lid,
base::ProcessId pid,
const std::string& url,
const std::string& rtc_configuration,
const std::string& constraints) {
OnPeerConnectionAdded(frame_id, lid, base::NullCallback());
}
void WebRtcEventLogManager::OnPeerConnectionRemoved(
content::GlobalRenderFrameHostId frame_id,
int lid) {
OnPeerConnectionRemoved(frame_id, lid, base::NullCallback());
}
void WebRtcEventLogManager::OnPeerConnectionUpdated(
content::GlobalRenderFrameHostId frame_id,
int lid,
const std::string& type,
const std::string& value) {
// TODO(810383): Get rid of magic value.
if (type == "stop") {
OnPeerConnectionStopped(frame_id, lid, base::NullCallback());
}
}
void WebRtcEventLogManager::OnPeerConnectionSessionIdSet(
content::GlobalRenderFrameHostId frame_id,
int lid,
const std::string& session_id) {
OnPeerConnectionSessionIdSet(frame_id, lid, session_id, base::NullCallback());
}
void WebRtcEventLogManager::OnWebRtcEventLogWrite(
content::GlobalRenderFrameHostId frame_id,
int lid,
const std::string& message) {
OnWebRtcEventLogWrite(frame_id, lid, message, base::NullCallback());
}
void WebRtcEventLogManager::EnableLocalLogging(
const base::FilePath& base_path) {
EnableLocalLogging(base_path, base::NullCallback());
}
void WebRtcEventLogManager::DisableLocalLogging() {
DisableLocalLogging(base::NullCallback());
}
void WebRtcEventLogManager::StartRemoteLogging(
int render_process_id,
const std::string& session_id,
size_t max_file_size_bytes,
int output_period_ms,
size_t web_app_id,
base::OnceCallback<void(bool, const std::string&, const std::string&)>
reply) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(reply);
BrowserContext* browser_context = GetBrowserContext(render_process_id);
const char* error = nullptr;
if (!browser_context) {
// RPH died before processing of this notification.
UmaRecordWebRtcEventLoggingApi(WebRtcEventLoggingApiUma::kDeadRph);
error = kStartRemoteLoggingFailureDeadRenderProcessHost;
} else if (!IsRemoteLoggingAllowedForBrowserContext(browser_context)) {
UmaRecordWebRtcEventLoggingApi(WebRtcEventLoggingApiUma::kFeatureDisabled);
error = kStartRemoteLoggingFailureFeatureDisabled;
} else if (browser_context->IsOffTheRecord()) {
// Feature disable in incognito. Since the feature can be disabled for
// non-incognito sessions, this should not expose incognito mode.
UmaRecordWebRtcEventLoggingApi(WebRtcEventLoggingApiUma::kIncognito);
error = kStartRemoteLoggingFailureFeatureDisabled;
}
if (error) {
content::GetUIThreadTaskRunner({})->PostTask(
FROM_HERE, base::BindOnce(std::move(reply), false, std::string(),
std::string(error)));
return;
}
const auto browser_context_id = GetBrowserContextId(browser_context);
DCHECK_NE(browser_context_id, kNullBrowserContextId);
// |this| is destroyed by ~BrowserProcessImpl(), so base::Unretained(this)
// will not be dereferenced after destruction.
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&WebRtcEventLogManager::StartRemoteLoggingInternal,
base::Unretained(this), render_process_id,
browser_context_id, session_id, browser_context->GetPath(),
max_file_size_bytes, output_period_ms, web_app_id,
std::move(reply)));
}
void WebRtcEventLogManager::ClearCacheForBrowserContext(
const BrowserContext* browser_context,
const base::Time& delete_begin,
const base::Time& delete_end,
base::OnceClosure reply) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
const auto browser_context_id = GetBrowserContextId(browser_context);
DCHECK_NE(browser_context_id, kNullBrowserContextId);
DCHECK_LT(num_user_blocking_tasks_, std::numeric_limits<size_t>::max());
if (++num_user_blocking_tasks_ == 1) {
task_runner_->UpdatePriority(base::TaskPriority::USER_BLOCKING);
}
// |this| is destroyed by ~BrowserProcessImpl(), so base::Unretained(this)
// will not be dereferenced after destruction.
task_runner_->PostTaskAndReply(
FROM_HERE,
base::BindOnce(
&WebRtcEventLogManager::ClearCacheForBrowserContextInternal,
base::Unretained(this), browser_context_id, delete_begin, delete_end),
base::BindOnce(
&WebRtcEventLogManager::OnClearCacheForBrowserContextDoneInternal,
base::Unretained(this), std::move(reply)));
}
void WebRtcEventLogManager::GetHistory(
BrowserContextId browser_context_id,
base::OnceCallback<void(const std::vector<UploadList::UploadInfo>&)>
reply) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(reply);
// |this| is destroyed by ~BrowserProcessImpl(), so base::Unretained(this)
// will not be dereferenced after destruction.
task_runner_->PostTask(
FROM_HERE, base::BindOnce(&WebRtcEventLogManager::GetHistoryInternal,
base::Unretained(this), browser_context_id,
std::move(reply)));
}
void WebRtcEventLogManager::SetLocalLogsObserver(
WebRtcLocalEventLogsObserver* observer,
base::OnceClosure reply) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// |this| is destroyed by ~BrowserProcessImpl(), so base::Unretained(this)
// will not be dereferenced after destruction.
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);
// |this| is destroyed by ~BrowserProcessImpl(), so base::Unretained(this)
// will not be dereferenced after destruction.
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&WebRtcEventLogManager::SetRemoteLogsObserverInternal,
base::Unretained(this), observer, std::move(reply)));
}
bool WebRtcEventLogManager::IsRemoteLoggingAllowedForBrowserContext(
BrowserContext* browser_context) const {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(browser_context);
if (!remote_logging_feature_enabled_) {
return false;
}
const Profile* profile = Profile::FromBrowserContext(browser_context);
DCHECK(profile);
const PrefService::Preference* webrtc_event_log_collection_allowed_pref =
profile->GetPrefs()->FindPreference(
prefs::kWebRtcEventLogCollectionAllowed);
DCHECK(webrtc_event_log_collection_allowed_pref);
if (webrtc_event_log_collection_allowed_pref->IsDefaultValue()) {
// The pref has not been set. GetBoolean would only return the default
// value. However, there is no single default value,
// because it depends on whether the profile receives cloud-based
// enterprise policies.
return DoesProfileDefaultToLoggingEnabled(profile);
}
// There is a non-default value set, so this value is authoritative.
return profile->GetPrefs()->GetBoolean(
prefs::kWebRtcEventLogCollectionAllowed);
}
std::unique_ptr<LogFileWriter::Factory>
WebRtcEventLogManager::CreateRemoteLogFileWriterFactory() {
if (remote_log_file_writer_factory_for_testing_) {
return std::move(remote_log_file_writer_factory_for_testing_);
#if !BUILDFLAG(IS_ANDROID)
} else if (base::FeatureList::IsEnabled(
features::kWebRtcRemoteEventLogGzipped)) {
return std::make_unique<GzippedLogFileWriterFactory>(
std::make_unique<GzipLogCompressorFactory>(
std::make_unique<DefaultGzippedSizeEstimator::Factory>()));
#endif
} else {
return std::make_unique<BaseLogFileWriterFactory>();
}
}
void WebRtcEventLogManager::RenderProcessExited(
RenderProcessHost* host,
const content::ChildProcessTerminationInfo& info) {
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);
// |this| is destroyed by ~BrowserProcessImpl(), so base::Unretained(this)
// will not be dereferenced after destruction.
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&WebRtcEventLogManager::RenderProcessExitedInternal,
base::Unretained(this), host->GetID()));
}
void WebRtcEventLogManager::OnPeerConnectionAdded(
content::GlobalRenderFrameHostId frame_id,
int lid,
base::OnceCallback<void(bool)> reply) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// TODO(crbug.com/1178670): Should this look at RFH shutdown instead of RPH?
RenderProcessHost* rph = RenderProcessHost::FromID(frame_id.child_id);
if (!rph) {
// RPH died before processing of this notification.
MaybeReply(FROM_HERE, 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);
// |this| is destroyed by ~BrowserProcessImpl(), so base::Unretained(this)
// will not be dereferenced after destruction.
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&WebRtcEventLogManager::OnPeerConnectionAddedInternal,
base::Unretained(this),
PeerConnectionKey(frame_id.child_id, lid, browser_context_id,
frame_id.frame_routing_id),
std::move(reply)));
}
void WebRtcEventLogManager::OnPeerConnectionRemoved(
content::GlobalRenderFrameHostId frame_id,
int lid,
base::OnceCallback<void(bool)> reply) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
const auto browser_context_id = GetBrowserContextId(frame_id.child_id);
if (browser_context_id == kNullBrowserContextId) {
// RPH died before processing of this notification. This is handled by
// RenderProcessExited() / RenderProcessHostDestroyed.
MaybeReply(FROM_HERE, std::move(reply), false);
return;
}
// |this| is destroyed by ~BrowserProcessImpl(), so base::Unretained(this)
// will not be dereferenced after destruction.
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&WebRtcEventLogManager::OnPeerConnectionRemovedInternal,
base::Unretained(this),
PeerConnectionKey(frame_id.child_id, lid, browser_context_id,
frame_id.frame_routing_id),
std::move(reply)));
}
void WebRtcEventLogManager::OnPeerConnectionStopped(
content::GlobalRenderFrameHostId frame_id,
int lid,
base::OnceCallback<void(bool)> reply) {
// From the logger's perspective, we treat stopping a peer connection the
// same as we do its removal. Should a stopped peer connection be later
// removed, the removal callback will assume the value |false|.
OnPeerConnectionRemoved(frame_id, lid, std::move(reply));
}
void WebRtcEventLogManager::OnPeerConnectionSessionIdSet(
content::GlobalRenderFrameHostId frame_id,
int lid,
const std::string& session_id,
base::OnceCallback<void(bool)> reply) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
const auto browser_context_id = GetBrowserContextId(frame_id.child_id);
if (browser_context_id == kNullBrowserContextId) {
// RPH died before processing of this notification. This is handled by
// RenderProcessExited() / RenderProcessHostDestroyed.
MaybeReply(FROM_HERE, std::move(reply), false);
return;
}
// |this| is destroyed by ~BrowserProcessImpl(), so base::Unretained(this)
// will not be dereferenced after destruction.
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&WebRtcEventLogManager::OnPeerConnectionSessionIdSetInternal,
base::Unretained(this),
PeerConnectionKey(frame_id.child_id, lid, browser_context_id,
frame_id.frame_routing_id),
session_id, std::move(reply)));
}
void WebRtcEventLogManager::OnWebRtcEventLogWrite(
content::GlobalRenderFrameHostId frame_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(frame_id.child_id);
if (!browser_context) {
// RPH died before processing of this notification.
MaybeReply(FROM_HERE, std::move(reply), std::make_pair(false, false));
return;
}
const auto browser_context_id = GetBrowserContextId(browser_context);
DCHECK_NE(browser_context_id, kNullBrowserContextId);
// |this| is destroyed by ~BrowserProcessImpl(), so base::Unretained(this)
// will not be dereferenced after destruction.
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&WebRtcEventLogManager::OnWebRtcEventLogWriteInternal,
base::Unretained(this),
PeerConnectionKey(frame_id.child_id, lid, browser_context_id,
frame_id.frame_routing_id),
message, std::move(reply)));
}
void WebRtcEventLogManager::EnableLocalLogging(
const base::FilePath& base_path,
base::OnceCallback<void(bool)> reply) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
EnableLocalLogging(base_path, kDefaultMaxLocalLogFileSizeBytes,
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());
// |this| is destroyed by ~BrowserProcessImpl(), so base::Unretained(this)
// will not be dereferenced after destruction.
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);
// |this| is destroyed by ~BrowserProcessImpl(), so base::Unretained(this)
// will not be dereferenced after destruction.
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&WebRtcEventLogManager::DisableLocalLoggingInternal,
base::Unretained(this), std::move(reply)));
}
void WebRtcEventLogManager::OnLocalLogStarted(PeerConnectionKey peer_connection,
const base::FilePath& file_path) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
OnLoggingTargetStarted(LoggingTarget::kLocalLogging, peer_connection,
/*output_period_ms=*/5000);
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,
int output_period_ms) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
OnLoggingTargetStarted(LoggingTarget::kRemoteLogging, key, output_period_ms);
if (remote_logs_observer_) {
remote_logs_observer_->OnRemoteLogStarted(key, file_path, output_period_ms);
}
}
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,
int output_period_ms) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
auto it = peer_connections_with_event_logging_enabled_in_webrtc_.find(key);
if (it != peer_connections_with_event_logging_enabled_in_webrtc_.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_in_webrtc_.emplace(key, target);
pc_tracker_proxy_->EnableWebRtcEventLogging(key, output_period_ms);
}
}
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_in_webrtc_.find(key);
CHECK(it != peer_connections_with_event_logging_enabled_in_webrtc_.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_in_webrtc_.erase(it);
pc_tracker_proxy_->DisableWebRtcEventLogging(key);
}
}
void WebRtcEventLogManager::StartListeningForPrefChangeForBrowserContext(
BrowserContext* browser_context) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(first_browser_context_initializations_done_);
CHECK(!browser_context->IsOffTheRecord());
const auto browser_context_id = GetBrowserContextId(browser_context);
auto it = pref_change_registrars_.emplace(std::piecewise_construct,
std::make_tuple(browser_context_id),
std::make_tuple());
DCHECK(it.second) << "Already listening.";
PrefChangeRegistrar& registrar = it.first->second;
Profile* profile = Profile::FromBrowserContext(browser_context);
DCHECK(profile);
registrar.Init(profile->GetPrefs());
// * |this| is destroyed by ~BrowserProcessImpl(), so base::Unretained(this)
// will not be dereferenced after destruction.
// * base::Unretained(browser_context) is safe, because |browser_context|
// stays alive until Chrome shut-down, at which point we'll stop listening
// as part of its (BrowserContext's) tear-down process.
registrar.Add(prefs::kWebRtcEventLogCollectionAllowed,
base::BindRepeating(&WebRtcEventLogManager::OnPrefChange,
base::Unretained(this),
base::Unretained(browser_context)));
}
void WebRtcEventLogManager::StopListeningForPrefChangeForBrowserContext(
BrowserContext* browser_context) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
const auto browser_context_id = GetBrowserContextId(browser_context);
size_t erased_count = pref_change_registrars_.erase(browser_context_id);
DCHECK_EQ(erased_count, 1u);
}
void WebRtcEventLogManager::OnPrefChange(BrowserContext* browser_context) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(first_browser_context_initializations_done_);
const Profile* profile = Profile::FromBrowserContext(browser_context);
DCHECK(profile);
const bool enabled = IsRemoteLoggingAllowedForBrowserContext(browser_context);
if (!enabled) {
// Dynamic refresh of the policy to DISABLED; stop ongoing logs, remove
// pending log files and stop any active uploads.
ClearCacheForBrowserContext(browser_context, base::Time::Min(),
base::Time::Max(), base::DoNothing());
}
// |this| is destroyed by ~BrowserProcessImpl(), so base::Unretained(this)
// will not be dereferenced after destruction.
base::OnceClosure task;
if (enabled) {
task = base::BindOnce(
&WebRtcEventLogManager::EnableRemoteBoundLoggingForBrowserContext,
base::Unretained(this), GetBrowserContextId(browser_context),
browser_context->GetPath(), base::OnceClosure());
} else {
task = base::BindOnce(
&WebRtcEventLogManager::DisableRemoteBoundLoggingForBrowserContext,
base::Unretained(this), GetBrowserContextId(browser_context),
base::OnceClosure());
}
task_runner_->PostTask(FROM_HERE, std::move(task));
}
void WebRtcEventLogManager::OnFirstBrowserContextLoaded() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
network::NetworkConnectionTracker* network_connection_tracker =
content::GetNetworkConnectionTracker();
DCHECK(network_connection_tracker);
auto log_file_writer_factory = CreateRemoteLogFileWriterFactory();
DCHECK(log_file_writer_factory);
// |network_connection_tracker| is owned by BrowserProcessImpl, which owns
// the IOThread. The internal task runner on which |this| uses
// |network_connection_tracker|, stops before IOThread dies, so we can trust
// that |network_connection_tracker| will not be used after destruction.
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&WebRtcEventLogManager::OnFirstBrowserContextLoadedInternal,
base::Unretained(this), base::Unretained(network_connection_tracker),
std::move(log_file_writer_factory)));
}
void WebRtcEventLogManager::OnFirstBrowserContextLoadedInternal(
network::NetworkConnectionTracker* network_connection_tracker,
std::unique_ptr<LogFileWriter::Factory> log_file_writer_factory) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
DCHECK(network_connection_tracker);
DCHECK(log_file_writer_factory);
remote_logs_manager_.SetNetworkConnectionTracker(network_connection_tracker);
remote_logs_manager_.SetLogFileWriterFactory(
std::move(log_file_writer_factory));
}
void WebRtcEventLogManager::EnableRemoteBoundLoggingForBrowserContext(
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);
MaybeReply(FROM_HERE, std::move(reply));
}
void WebRtcEventLogManager::DisableRemoteBoundLoggingForBrowserContext(
BrowserContextId browser_context_id,
base::OnceClosure reply) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
// Note that the BrowserContext might never have been enabled in the
// remote-bound manager; that's not a problem.
remote_logs_manager_.DisableForBrowserContext(browser_context_id);
MaybeReply(FROM_HERE, std::move(reply));
}
void WebRtcEventLogManager::
RemovePendingRemoteBoundLogsForNotEnabledBrowserContext(
BrowserContextId browser_context_id,
const base::FilePath& browser_context_dir,
base::OnceClosure reply) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
remote_logs_manager_.RemovePendingLogsForNotEnabledBrowserContext(
browser_context_id, browser_context_dir);
MaybeReply(FROM_HERE, std::move(reply));
}
void WebRtcEventLogManager::OnPeerConnectionAddedInternal(
PeerConnectionKey key,
base::OnceCallback<void(bool)> reply) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
const bool local_result = local_logs_manager_.OnPeerConnectionAdded(key);
const bool remote_result = remote_logs_manager_.OnPeerConnectionAdded(key);
DCHECK_EQ(local_result, remote_result);
MaybeReply(FROM_HERE, std::move(reply), local_result);
}
void WebRtcEventLogManager::OnPeerConnectionRemovedInternal(
PeerConnectionKey key,
base::OnceCallback<void(bool)> reply) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
const bool local_result = local_logs_manager_.OnPeerConnectionRemoved(key);
const bool remote_result = remote_logs_manager_.OnPeerConnectionRemoved(key);
DCHECK_EQ(local_result, remote_result);
MaybeReply(FROM_HERE, std::move(reply), local_result);
}
void WebRtcEventLogManager::OnPeerConnectionSessionIdSetInternal(
PeerConnectionKey key,
const std::string& session_id,
base::OnceCallback<void(bool)> reply) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
const bool result =
remote_logs_manager_.OnPeerConnectionSessionIdSet(key, session_id);
MaybeReply(FROM_HERE, std::move(reply), result);
}
void WebRtcEventLogManager::OnWebRtcEventLogWriteInternal(
PeerConnectionKey key,
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, message);
const bool remote_result = remote_logs_manager_.EventLogWrite(key, message);
MaybeReply(FROM_HERE, std::move(reply),
std::make_pair(local_result, remote_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);
MaybeReply(FROM_HERE, std::move(reply), result);
}
void WebRtcEventLogManager::DisableLocalLoggingInternal(
base::OnceCallback<void(bool)> reply) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
const bool result = local_logs_manager_.DisableLogging();
MaybeReply(FROM_HERE, std::move(reply), result);
}
void WebRtcEventLogManager::StartRemoteLoggingInternal(
int render_process_id,
BrowserContextId browser_context_id,
const std::string& session_id,
const base::FilePath& browser_context_dir,
size_t max_file_size_bytes,
int output_period_ms,
size_t web_app_id,
base::OnceCallback<void(bool, const std::string&, const std::string&)>
reply) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
std::string log_id;
std::string error_message;
const bool result = remote_logs_manager_.StartRemoteLogging(
render_process_id, browser_context_id, session_id, browser_context_dir,
max_file_size_bytes, output_period_ms, web_app_id, &log_id,
&error_message);
// |log_id| set only if successful; |error_message| set only if unsuccessful.
DCHECK_EQ(result, !log_id.empty());
DCHECK_EQ(!result, !error_message.empty());
content::GetUIThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(std::move(reply), result, log_id, error_message));
}
void WebRtcEventLogManager::ClearCacheForBrowserContextInternal(
BrowserContextId browser_context_id,
const base::Time& delete_begin,
const base::Time& delete_end) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
remote_logs_manager_.ClearCacheForBrowserContext(browser_context_id,
delete_begin, delete_end);
}
void WebRtcEventLogManager::OnClearCacheForBrowserContextDoneInternal(
base::OnceClosure reply) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK_GT(num_user_blocking_tasks_, 0u);
if (--num_user_blocking_tasks_ == 0) {
task_runner_->UpdatePriority(base::TaskPriority::BEST_EFFORT);
}
std::move(reply).Run();
}
void WebRtcEventLogManager::GetHistoryInternal(
BrowserContextId browser_context_id,
base::OnceCallback<void(const std::vector<UploadList::UploadInfo>&)>
reply) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
DCHECK(reply);
remote_logs_manager_.GetHistory(browser_context_id, std::move(reply));
}
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) {
content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE, std::move(reply));
}
}
void WebRtcEventLogManager::SetRemoteLogsObserverInternal(
WebRtcRemoteEventLogsObserver* observer,
base::OnceClosure reply) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
remote_logs_observer_ = observer;
if (reply) {
content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE, std::move(reply));
}
}
void WebRtcEventLogManager::SetClockForTesting(base::Clock* clock,
base::OnceClosure reply) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(reply);
auto task = [](WebRtcEventLogManager* manager, base::Clock* clock,
base::OnceClosure reply) {
manager->local_logs_manager_.SetClockForTesting(clock);
content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE, std::move(reply));
};
// |this| is destroyed by ~BrowserProcessImpl(), so base::Unretained(this)
// will not be dereferenced after destruction.
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);
DCHECK(reply);
auto task = [](WebRtcEventLogManager* manager,
std::unique_ptr<PeerConnectionTrackerProxy> pc_tracker_proxy,
base::OnceClosure reply) {
manager->pc_tracker_proxy_ = std::move(pc_tracker_proxy);
content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE, std::move(reply));
};
// |this| is destroyed by ~BrowserProcessImpl(), so base::Unretained(this)
// will not be dereferenced after destruction.
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);
DCHECK(reply);
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));
content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE,
std::move(reply));
};
// |this| is destroyed by ~BrowserProcessImpl(), so base::Unretained(this)
// will not be dereferenced after destruction.
task_runner_->PostTask(
FROM_HERE, base::BindOnce(task, base::Unretained(this),
std::move(uploader_factory), std::move(reply)));
}
void WebRtcEventLogManager::SetRemoteLogFileWriterFactoryForTesting(
std::unique_ptr<LogFileWriter::Factory> factory) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(!first_browser_context_initializations_done_) << "Too late.";
DCHECK(!remote_log_file_writer_factory_for_testing_) << "Already called.";
remote_log_file_writer_factory_for_testing_ = std::move(factory);
}
void WebRtcEventLogManager::UploadConditionsHoldForTesting(
base::OnceCallback<void(bool)> callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// Unit tests block until |callback| is sent back, so the use
// of base::Unretained(&remote_logs_manager_) is safe.
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&WebRtcRemoteEventLogManager::UploadConditionsHoldForTesting,
base::Unretained(&remote_logs_manager_), std::move(callback)));
}
scoped_refptr<base::SequencedTaskRunner>
WebRtcEventLogManager::GetTaskRunnerForTesting() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
return task_runner_;
}
void WebRtcEventLogManager::PostNullTaskForTesting(base::OnceClosure reply) {
task_runner_->PostTask(FROM_HERE, std::move(reply));
}
void WebRtcEventLogManager::ShutDownForTesting(base::OnceClosure reply) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// Unit tests block until |callback| is sent back, so the use
// of base::Unretained(&remote_logs_manager_) is safe.
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&WebRtcRemoteEventLogManager::ShutDownForTesting,
base::Unretained(&remote_logs_manager_),
std::move(reply)));
}
} // namespace webrtc_event_logging