blob: dafe9179271f0cd17fee71b4e4e4b53f51acf427 [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 "content/browser/net/network_quality_observer_impl.h"
#include "base/metrics/histogram_macros.h"
#include "content/browser/renderer_host/render_process_host_impl.h"
#include "content/common/view_messages.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/render_process_host.h"
#include "net/nqe/network_quality_estimator.h"
namespace {
// Returns true if the |current_value| is meaningfully different from the
// |past_value|. May be called with either RTT or throughput values to compare.
bool MetricChangedMeaningfully(int32_t past_value, int32_t current_value) {
if ((past_value == net::nqe::internal::INVALID_RTT_THROUGHPUT) !=
(current_value == net::nqe::internal::INVALID_RTT_THROUGHPUT)) {
return true;
}
if (past_value == net::nqe::internal::INVALID_RTT_THROUGHPUT &&
current_value == net::nqe::internal::INVALID_RTT_THROUGHPUT) {
return false;
}
DCHECK_LE(0, past_value);
DCHECK_LE(0, current_value);
// Metric has changed meaningfully only if (i) the difference between the two
// values exceed the threshold; and, (ii) the ratio of the values also exceeds
// the threshold.
static const int kMinDifferenceInMetrics = 100;
static const float kMinRatio = 1.2f;
if (std::abs(past_value - current_value) < kMinDifferenceInMetrics) {
// The absolute change in the value is not sufficient enough.
return false;
}
if (past_value < (kMinRatio * current_value) &&
current_value < (kMinRatio * past_value)) {
// The relative change in the value is not sufficient enough.
return false;
}
return true;
}
} // namespace
namespace content {
// UiThreadObserver observes the changes to the network quality on the UI
// thread, and notifies the renderers of the change in the network quality.
class NetworkQualityObserverImpl::UiThreadObserver
: public content::NotificationObserver {
public:
UiThreadObserver()
: last_notified_type_(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN) {}
~UiThreadObserver() override {
if (!registrar_.IsRegistered(this, NOTIFICATION_RENDERER_PROCESS_CREATED,
NotificationService::AllSources())) {
return;
}
registrar_.Remove(this, NOTIFICATION_RENDERER_PROCESS_CREATED,
NotificationService::AllSources());
}
void InitOnUIThread() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
registrar_.Add(this, NOTIFICATION_RENDERER_PROCESS_CREATED,
NotificationService::AllSources());
}
void OnEffectiveConnectionTypeChanged(net::EffectiveConnectionType type) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (last_notified_type_ == type)
return;
last_notified_type_ = type;
// Notify all the existing renderers of the change in the network quality.
for (RenderProcessHost::iterator it(RenderProcessHost::AllHostsIterator());
!it.IsAtEnd(); it.Advance()) {
if (it.GetCurrentValue()->IsInitializedAndNotDead()) {
it.GetCurrentValue()->GetRendererInterface()->OnNetworkQualityChanged(
last_notified_type_, last_notified_network_quality_.http_rtt(),
last_notified_network_quality_.transport_rtt(),
last_notified_network_quality_.downstream_throughput_kbps());
}
}
}
void OnRTTOrThroughputEstimatesComputed(
const net::nqe::internal::NetworkQuality& network_quality) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
last_notified_network_quality_ = network_quality;
// Notify all the existing renderers of the change in the network quality.
for (RenderProcessHost::iterator it(RenderProcessHost::AllHostsIterator());
!it.IsAtEnd(); it.Advance()) {
if (it.GetCurrentValue()->IsInitializedAndNotDead()) {
it.GetCurrentValue()->GetRendererInterface()->OnNetworkQualityChanged(
last_notified_type_, last_notified_network_quality_.http_rtt(),
last_notified_network_quality_.transport_rtt(),
last_notified_network_quality_.downstream_throughput_kbps());
}
}
}
private:
// NotificationObserver implementation:
void Observe(int type,
const NotificationSource& source,
const NotificationDetails& details) override {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK_EQ(NOTIFICATION_RENDERER_PROCESS_CREATED, type);
RenderProcessHost* rph = Source<RenderProcessHost>(source).ptr();
// Notify the newly created renderer of the current network quality.
rph->GetRendererInterface()->OnNetworkQualityChanged(
last_notified_type_, last_notified_network_quality_.http_rtt(),
last_notified_network_quality_.transport_rtt(),
last_notified_network_quality_.downstream_throughput_kbps());
}
content::NotificationRegistrar registrar_;
// The network quality that was last sent to the renderers.
net::EffectiveConnectionType last_notified_type_;
net::nqe::internal::NetworkQuality last_notified_network_quality_;
DISALLOW_COPY_AND_ASSIGN(UiThreadObserver);
};
NetworkQualityObserverImpl::NetworkQualityObserverImpl(
net::NetworkQualityEstimator* network_quality_estimator)
: network_quality_estimator_(network_quality_estimator),
last_notified_type_(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN) {
network_quality_estimator_->AddRTTAndThroughputEstimatesObserver(this);
network_quality_estimator_->AddEffectiveConnectionTypeObserver(this);
ui_thread_observer_ = std::make_unique<UiThreadObserver>();
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::BindOnce(&UiThreadObserver::InitOnUIThread,
base::Unretained(ui_thread_observer_.get())));
}
NetworkQualityObserverImpl::~NetworkQualityObserverImpl() {
DCHECK(thread_checker_.CalledOnValidThread());
network_quality_estimator_->RemoveRTTAndThroughputEstimatesObserver(this);
network_quality_estimator_->RemoveEffectiveConnectionTypeObserver(this);
DCHECK(ui_thread_observer_);
// If possible, delete |ui_thread_observer_| on UI thread.
UiThreadObserver* ui_thread_observer_ptr = ui_thread_observer_.release();
bool posted = BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE,
ui_thread_observer_ptr);
if (!posted)
delete ui_thread_observer_ptr;
}
void NetworkQualityObserverImpl::OnEffectiveConnectionTypeChanged(
net::EffectiveConnectionType type) {
DCHECK(thread_checker_.CalledOnValidThread());
if (last_notified_type_ == type)
return;
last_notified_type_ = type;
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::BindOnce(&UiThreadObserver::OnEffectiveConnectionTypeChanged,
base::Unretained(ui_thread_observer_.get()),
last_notified_type_));
}
void NetworkQualityObserverImpl::OnRTTOrThroughputEstimatesComputed(
base::TimeDelta http_rtt,
base::TimeDelta transport_rtt,
int32_t downstream_throughput_kbps) {
DCHECK(thread_checker_.CalledOnValidThread());
// Check if any of the network quality metrics changed meaningfully.
bool http_rtt_changed = MetricChangedMeaningfully(
last_notified_network_quality_.http_rtt().InMilliseconds(),
http_rtt.InMilliseconds());
bool transport_rtt_changed = MetricChangedMeaningfully(
last_notified_network_quality_.transport_rtt().InMilliseconds(),
transport_rtt.InMilliseconds());
bool kbps_changed = MetricChangedMeaningfully(
last_notified_network_quality_.downstream_throughput_kbps(),
downstream_throughput_kbps);
bool network_quality_meaningfully_changed =
http_rtt_changed || transport_rtt_changed || kbps_changed;
UMA_HISTOGRAM_BOOLEAN("NQE.ContentObserver.NetworkQualityMeaningfullyChanged",
network_quality_meaningfully_changed);
if (!network_quality_meaningfully_changed) {
// Return since none of the metrics changed meaningfully. This reduces
// the number of notifications to the different renderers every time
// the network quality is recomputed.
return;
}
last_notified_network_quality_ = net::nqe::internal::NetworkQuality(
http_rtt, transport_rtt, downstream_throughput_kbps);
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::BindOnce(&UiThreadObserver::OnRTTOrThroughputEstimatesComputed,
base::Unretained(ui_thread_observer_.get()),
last_notified_network_quality_));
}
std::unique_ptr<net::RTTAndThroughputEstimatesObserver>
CreateNetworkQualityObserver(
net::NetworkQualityEstimator* network_quality_estimator) {
return std::make_unique<NetworkQualityObserverImpl>(
network_quality_estimator);
}
} // namespace content