Remove QNetworkStatusMonitor

Since the old code is now fully integrated in QNetworkInformation backends

Pick-to: dev
Change-Id: Ia843d17bb3c98333e8d68752e25722b5860f48e0
Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
This commit is contained in:
Mårten Nordheim 2021-01-28 13:11:08 +01:00
parent 309a2360fd
commit 7860b9e6ff
7 changed files with 23 additions and 519 deletions

View File

@ -1312,7 +1312,7 @@ QHttpNetworkConnection::QHttpNetworkConnection(const QString &hostName, quint16
{ {
Q_D(QHttpNetworkConnection); Q_D(QHttpNetworkConnection);
d->init(); d->init();
if (QNetworkStatusMonitor::isEnabled()) { if (QNetworkConnectionMonitor::isEnabled()) {
connect(&d->connectionMonitor, &QNetworkConnectionMonitor::reachabilityChanged, connect(&d->connectionMonitor, &QNetworkConnectionMonitor::reachabilityChanged,
this, &QHttpNetworkConnection::onlineStateChanged, Qt::QueuedConnection); this, &QHttpNetworkConnection::onlineStateChanged, Qt::QueuedConnection);
} }
@ -1326,7 +1326,7 @@ QHttpNetworkConnection::QHttpNetworkConnection(quint16 connectionCount, const QS
{ {
Q_D(QHttpNetworkConnection); Q_D(QHttpNetworkConnection);
d->init(); d->init();
if (QNetworkStatusMonitor::isEnabled()) { if (QNetworkConnectionMonitor::isEnabled()) {
connect(&d->connectionMonitor, &QNetworkConnectionMonitor::reachabilityChanged, connect(&d->connectionMonitor, &QNetworkConnectionMonitor::reachabilityChanged,
this, &QHttpNetworkConnection::onlineStateChanged, Qt::QueuedConnection); this, &QHttpNetworkConnection::onlineStateChanged, Qt::QueuedConnection);
} }

View File

@ -911,7 +911,7 @@ void QHttpNetworkConnectionChannel::_q_connected()
pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningSupportUnknown; pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningSupportUnknown;
if (QNetworkStatusMonitor::isEnabled()) { if (QNetworkConnectionMonitor::isEnabled()) {
auto connectionPrivate = connection->d_func(); auto connectionPrivate = connection->d_func();
if (!connectionPrivate->connectionMonitor.isMonitoring()) { if (!connectionPrivate->connectionMonitor.isMonitoring()) {
// Now that we have a pair of addresses, we can start monitoring the // Now that we have a pair of addresses, we can start monitoring the

View File

@ -208,7 +208,7 @@ bool QNetworkConnectionMonitor::setTargets(const QHostAddress &local, const QHos
qt_sockaddr client = qt_hostaddress_to_sockaddr(local); qt_sockaddr client = qt_hostaddress_to_sockaddr(local);
if (remote.isNull()) { if (remote.isNull()) {
// That's a special case our QNetworkStatusMonitor is using (AnyIpv4/6 address to check an overall status). // That's a special case our QNetworkInformation backend is using (AnyIpv4/6 address to check an overall status).
d->probe = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, reinterpret_cast<sockaddr *>(&client)); d->probe = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, reinterpret_cast<sockaddr *>(&client));
} else { } else {
qt_sockaddr target = qt_hostaddress_to_sockaddr(remote); qt_sockaddr target = qt_hostaddress_to_sockaddr(remote);
@ -301,124 +301,9 @@ bool QNetworkConnectionMonitor::isReachable()
return d->isReachable(); return d->isReachable();
} }
class QNetworkStatusMonitorPrivate : public QObjectPrivate bool QNetworkConnectionMonitor::isEnabled()
{
public:
QNetworkConnectionMonitor ipv4Probe;
bool isOnlineIpv4 = false;
QNetworkConnectionMonitor ipv6Probe;
bool isOnlineIpv6 = false;
};
QNetworkStatusMonitor::QNetworkStatusMonitor(QObject *parent)
: QObject(*new QNetworkStatusMonitorPrivate, parent)
{
Q_D(QNetworkStatusMonitor);
if (d->ipv4Probe.setTargets(QHostAddress::AnyIPv4, {})) {
// We manage to create SCNetworkReachabilityRef for IPv4, let's
// read the last known state then!
d->isOnlineIpv4 = d->ipv4Probe.isReachable();
}
if (d->ipv6Probe.setTargets(QHostAddress::AnyIPv6, {})) {
// We manage to create SCNetworkReachability ref for IPv6, let's
// read the last known state then!
d->isOnlineIpv6 = d->ipv6Probe.isReachable();
}
connect(&d->ipv4Probe, &QNetworkConnectionMonitor::reachabilityChanged, this,
&QNetworkStatusMonitor::reachabilityChanged, Qt::QueuedConnection);
connect(&d->ipv6Probe, &QNetworkConnectionMonitor::reachabilityChanged, this,
&QNetworkStatusMonitor::reachabilityChanged, Qt::QueuedConnection);
}
QNetworkStatusMonitor::~QNetworkStatusMonitor()
{
Q_D(QNetworkStatusMonitor);
d->ipv4Probe.disconnect();
d->ipv4Probe.stopMonitoring();
d->ipv6Probe.disconnect();
d->ipv6Probe.stopMonitoring();
}
bool QNetworkStatusMonitor::start()
{
Q_D(QNetworkStatusMonitor);
if (isMonitoring()) {
qCWarning(lcNetMon, "Network status monitor is already active");
return true;
}
d->ipv4Probe.startMonitoring();
d->ipv6Probe.startMonitoring();
return isMonitoring();
}
void QNetworkStatusMonitor::stop()
{
Q_D(QNetworkStatusMonitor);
if (d->ipv4Probe.isMonitoring())
d->ipv4Probe.stopMonitoring();
if (d->ipv6Probe.isMonitoring())
d->ipv6Probe.stopMonitoring();
}
bool QNetworkStatusMonitor::isMonitoring() const
{
Q_D(const QNetworkStatusMonitor);
return d->ipv4Probe.isMonitoring() || d->ipv6Probe.isMonitoring();
}
bool QNetworkStatusMonitor::isNetworkAccessible()
{
// This function is to be executed on the thread that created
// and uses 'this'.
Q_D(QNetworkStatusMonitor);
return d->isOnlineIpv4 || d->isOnlineIpv6;
}
bool QNetworkStatusMonitor::event(QEvent *event)
{
return QObject::event(event);
}
bool QNetworkStatusMonitor::isEnabled()
{ {
return true; return true;
} }
void QNetworkStatusMonitor::reachabilityChanged(bool online)
{
// This function is executed on the thread that created/uses 'this',
// not on the reachability queue.
Q_D(QNetworkStatusMonitor);
auto probe = qobject_cast<QNetworkConnectionMonitor *>(sender());
if (!probe)
return;
const bool isIpv4 = probe == &d->ipv4Probe;
bool &probeOnline = isIpv4 ? d->isOnlineIpv4 : d->isOnlineIpv6;
bool otherOnline = isIpv4 ? d->isOnlineIpv6 : d->isOnlineIpv4;
if (probeOnline == online) {
// We knew this already?
return;
}
probeOnline = online;
if (!otherOnline) {
// We either just lost or got a network access.
emit onlineStateChanged(probeOnline);
}
}
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -80,6 +80,8 @@ public:
bool isMonitoring() const; bool isMonitoring() const;
void stopMonitoring(); void stopMonitoring();
static bool isEnabled();
Q_SIGNALS: Q_SIGNALS:
// Important: connect to this using QueuedConnection. On Darwin // Important: connect to this using QueuedConnection. On Darwin
// callback is coming on a special dispatch queue. // callback is coming on a special dispatch queue.
@ -90,37 +92,6 @@ private:
Q_DISABLE_COPY_MOVE(QNetworkConnectionMonitor) Q_DISABLE_COPY_MOVE(QNetworkConnectionMonitor)
}; };
class QNetworkStatusMonitorPrivate;
class Q_AUTOTEST_EXPORT QNetworkStatusMonitor : public QObject
{
Q_OBJECT
public:
QNetworkStatusMonitor(QObject *parent);
~QNetworkStatusMonitor();
bool isNetworkAccessible();
bool start();
void stop();
bool isMonitoring() const;
bool event(QEvent *event) override;
static bool isEnabled();
Q_SIGNALS:
// Unlike QNetworkConnectionMonitor, this can be connected to directly.
void onlineStateChanged(bool isOnline);
private slots:
void reachabilityChanged(bool isOnline);
private:
Q_DECLARE_PRIVATE(QNetworkStatusMonitor)
Q_DISABLE_COPY_MOVE(QNetworkStatusMonitor)
};
Q_DECLARE_LOGGING_CATEGORY(lcNetMon) Q_DECLARE_LOGGING_CATEGORY(lcNetMon)
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -45,7 +45,7 @@ QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcNetMon, "qt.network.monitor"); Q_LOGGING_CATEGORY(lcNetMon, "qt.network.monitor");
// Note: this 'stub' version is never enabled (see QNetworkStatusMonitor::isEnabled below) // Note: this 'stub' version is never enabled (see QNetworkConnectionMonitor::isEnabled below)
// and thus should never affect QNAM in any unusuall way. Having this 'stub' version is similar // and thus should never affect QNAM in any unusuall way. Having this 'stub' version is similar
// to building Qt with bearer management configured out. // to building Qt with bearer management configured out.
@ -96,51 +96,9 @@ bool QNetworkConnectionMonitor::isReachable()
return false; return false;
} }
class QNetworkStatusMonitorPrivate : public QObjectPrivate bool QNetworkConnectionMonitor::isEnabled()
{
};
QNetworkStatusMonitor::QNetworkStatusMonitor(QObject *parent)
: QObject(*new QNetworkStatusMonitorPrivate, parent)
{
}
QNetworkStatusMonitor::~QNetworkStatusMonitor()
{
}
bool QNetworkStatusMonitor::start()
{ {
return false; return false;
} }
void QNetworkStatusMonitor::stop()
{
}
bool QNetworkStatusMonitor::isMonitoring() const
{
return false;
}
bool QNetworkStatusMonitor::isNetworkAccessible()
{
return false;
}
bool QNetworkStatusMonitor::event(QEvent *event)
{
return QObject::event(event);
}
bool QNetworkStatusMonitor::isEnabled()
{
return false;
}
void QNetworkStatusMonitor::reachabilityChanged(bool online)
{
Q_UNUSED(online);
}
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -490,280 +490,9 @@ bool QNetworkConnectionMonitor::isReachable()
return d_func()->connectivity & required; return d_func()->connectivity & required;
} }
class QNetworkListManagerEvents : public INetworkListManagerEvents bool QNetworkConnectionMonitor::isEnabled()
{
public:
QNetworkListManagerEvents(QNetworkStatusMonitorPrivate *monitor);
virtual ~QNetworkListManagerEvents();
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) override;
ULONG STDMETHODCALLTYPE AddRef() override { return ++ref; }
ULONG STDMETHODCALLTYPE Release() override
{
if (--ref == 0) {
delete this;
return 0;
}
return ref;
}
HRESULT STDMETHODCALLTYPE ConnectivityChanged(NLM_CONNECTIVITY newConnectivity) override;
[[nodiscard]]
bool start();
bool stop();
private:
ComPtr<INetworkListManager> networkListManager = nullptr;
ComPtr<IConnectionPoint> connectionPoint = nullptr;
QNetworkStatusMonitorPrivate *monitor = nullptr;
QAtomicInteger<ULONG> ref = 0;
DWORD cookie = 0;
};
class QNetworkStatusMonitorPrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QNetworkStatusMonitor);
public:
QNetworkStatusMonitorPrivate();
~QNetworkStatusMonitorPrivate();
[[nodiscard]]
bool start();
void stop();
void setConnectivity(NLM_CONNECTIVITY newConnectivity);
private:
friend class QNetworkListManagerEvents;
ComPtr<QNetworkListManagerEvents> managerEvents;
NLM_CONNECTIVITY connectivity = NLM_CONNECTIVITY_DISCONNECTED;
bool monitoring = false;
bool comInitFailed = false;
};
QNetworkListManagerEvents::QNetworkListManagerEvents(QNetworkStatusMonitorPrivate *monitor)
: monitor(monitor)
{
auto hr = CoCreateInstance(CLSID_NetworkListManager, nullptr, CLSCTX_INPROC_SERVER,
IID_INetworkListManager, &networkListManager);
if (FAILED(hr)) {
qCWarning(lcNetMon) << "Could not get a NetworkListManager instance:"
<< errorStringFromHResult(hr);
return;
}
// Set initial connectivity
hr = networkListManager->GetConnectivity(&monitor->connectivity);
if (FAILED(hr))
qCWarning(lcNetMon) << "Could not get connectivity:" << errorStringFromHResult(hr);
ComPtr<IConnectionPointContainer> connectionPointContainer;
hr = networkListManager.As(&connectionPointContainer);
if (SUCCEEDED(hr)) {
hr = connectionPointContainer->FindConnectionPoint(IID_INetworkListManagerEvents,
&connectionPoint);
}
if (FAILED(hr)) {
qCWarning(lcNetMon) << "Failed to get connection point for network list manager events:"
<< errorStringFromHResult(hr);
}
}
QNetworkListManagerEvents::~QNetworkListManagerEvents()
{
Q_ASSERT(ref == 0);
}
HRESULT STDMETHODCALLTYPE QNetworkListManagerEvents::QueryInterface(REFIID riid, void **ppvObject)
{
if (!ppvObject)
return E_INVALIDARG;
return QueryInterfaceImpl<IUnknown>(this, riid, ppvObject)
|| QueryInterfaceImpl<INetworkListManagerEvents>(this, riid, ppvObject)
? S_OK
: E_NOINTERFACE;
}
HRESULT STDMETHODCALLTYPE
QNetworkListManagerEvents::ConnectivityChanged(NLM_CONNECTIVITY newConnectivity)
{
// This function is run on a different thread than 'monitor' is created on, so we need to run
// it on that thread
QMetaObject::invokeMethod(monitor->q_ptr,
[newConnectivity, monitor = this->monitor]() {
monitor->setConnectivity(newConnectivity);
},
Qt::QueuedConnection);
return S_OK;
}
bool QNetworkListManagerEvents::start()
{
if (!connectionPoint) {
qCWarning(lcNetMon, "Initialization failed, can't start!");
return false;
}
auto hr = connectionPoint->Advise(this, &cookie);
if (FAILED(hr)) {
qCWarning(lcNetMon) << "Failed to subscribe to network connectivity events:"
<< errorStringFromHResult(hr);
return false;
}
// Update connectivity since it might have changed since this class was constructed
NLM_CONNECTIVITY connectivity;
hr = networkListManager->GetConnectivity(&connectivity);
if (FAILED(hr))
qCWarning(lcNetMon) << "Could not get connectivity:" << errorStringFromHResult(hr);
else
monitor->setConnectivity(connectivity);
return true;
}
bool QNetworkListManagerEvents::stop()
{
Q_ASSERT(connectionPoint);
auto hr = connectionPoint->Unadvise(cookie);
if (FAILED(hr)) {
qCWarning(lcNetMon) << "Failed to unsubscribe from network connectivity events:"
<< errorStringFromHResult(hr);
return false;
}
cookie = 0;
return true;
}
QNetworkStatusMonitorPrivate::QNetworkStatusMonitorPrivate()
{
auto hr = CoInitialize(nullptr);
if (FAILED(hr)) {
qCWarning(lcNetMon) << "Failed to initialize COM:" << errorStringFromHResult(hr);
comInitFailed = true;
return;
}
managerEvents = new QNetworkListManagerEvents(this);
}
QNetworkStatusMonitorPrivate::~QNetworkStatusMonitorPrivate()
{
if (comInitFailed)
return;
if (monitoring)
stop();
}
void QNetworkStatusMonitorPrivate::setConnectivity(NLM_CONNECTIVITY newConnectivity)
{
Q_Q(QNetworkStatusMonitor);
const bool oldAccessibility = q->isNetworkAccessible();
connectivity = newConnectivity;
const bool accessibility = q->isNetworkAccessible();
if (oldAccessibility != accessibility)
emit q->onlineStateChanged(accessibility);
}
bool QNetworkStatusMonitorPrivate::start()
{
Q_ASSERT(!monitoring);
if (comInitFailed) {
auto hr = CoInitialize(nullptr);
if (FAILED(hr)) {
qCWarning(lcNetMon) << "Failed to initialize COM:" << errorStringFromHResult(hr);
comInitFailed = true;
return false;
}
comInitFailed = false;
}
if (!managerEvents)
managerEvents = new QNetworkListManagerEvents(this);
if (managerEvents->start())
monitoring = true;
return monitoring;
}
void QNetworkStatusMonitorPrivate::stop()
{
Q_ASSERT(managerEvents);
Q_ASSERT(monitoring);
// Can return false but realistically shouldn't since that would break everything:
managerEvents->stop();
monitoring = false;
managerEvents.Reset();
CoUninitialize();
comInitFailed = true; // we check this value in start() to see if we need to re-initialize
}
QNetworkStatusMonitor::QNetworkStatusMonitor(QObject *parent)
: QObject(*new QNetworkStatusMonitorPrivate, parent)
{
}
QNetworkStatusMonitor::~QNetworkStatusMonitor() {}
bool QNetworkStatusMonitor::start()
{
if (isMonitoring()) {
qCWarning(lcNetMon, "Monitor is already active, call stopMonitoring() first");
return false;
}
return d_func()->start();
}
void QNetworkStatusMonitor::stop()
{
if (!isMonitoring()) {
qCWarning(lcNetMon, "stopMonitoring was called when not monitoring!");
return;
}
d_func()->stop();
}
bool QNetworkStatusMonitor::isMonitoring() const
{
return d_func()->monitoring;
}
bool QNetworkStatusMonitor::isNetworkAccessible()
{
return d_func()->connectivity
& (NLM_CONNECTIVITY_IPV4_INTERNET | NLM_CONNECTIVITY_IPV6_INTERNET
| NLM_CONNECTIVITY_IPV4_SUBNET | NLM_CONNECTIVITY_IPV6_SUBNET
| NLM_CONNECTIVITY_IPV4_LOCALNETWORK | NLM_CONNECTIVITY_IPV6_LOCALNETWORK);
}
bool QNetworkStatusMonitor::event(QEvent *event)
{
if (event->type() == QEvent::ThreadChange && isMonitoring()) {
stop();
QMetaObject::invokeMethod(this, &QNetworkStatusMonitor::start, Qt::QueuedConnection);
}
return QObject::event(event);
}
bool QNetworkStatusMonitor::isEnabled()
{ {
return true; return true;
} }
void QNetworkStatusMonitor::reachabilityChanged(bool online)
{
Q_UNUSED(online);
Q_UNREACHABLE();
}
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -30,19 +30,19 @@
#include <QtCore/qdeadlinetimer.h> #include <QtCore/qdeadlinetimer.h>
#include <QtNetwork/qhostinfo.h> #include <QtNetwork/qhostinfo.h>
#include <QtNetwork/qnetworkinformation.h>
#include <QtNetwork/private/qnetconmonitor_p.h> #include <QtNetwork/private/qnetconmonitor_p.h>
#include <QtTest/qsignalspy.h> #include <QtTest/qsignalspy.h>
void testDetectDisconnection();
void testDetectRouteDisrupted(); void testDetectRouteDisrupted();
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
QCoreApplication app(argc, argv); QCoreApplication app(argc, argv);
if (!QNetworkStatusMonitor::isEnabled()) { if (!QNetworkConnectionMonitor::isEnabled()) {
qWarning("QNetworkStatusMonitor is not enabled for this platform!"); qWarning("QNetworkConnectionMonitor is not enabled for this platform!");
return 0; return 0;
} }
@ -51,10 +51,8 @@ int main(int argc, char *argv[])
QByteArray indent(" "); QByteArray indent(" ");
{ {
QTextStream writer(stdout); QTextStream writer(stdout);
writer << "Manual test for QNetwork{Status|Connection}Monitor\n" writer << "Manual test for QNetworkConnection}Monitor\n"
<< "The tests are grouped by what they test. Run them in any order\n" << "The tests are grouped by what they test. Run them in any order\n"
<< "- QNetworkStatusMonitor tests:\n"
<< indent << "c" << indent << "Test connection and disconnection detection.\n"
<< "- QNetworkConnectionMonitor tests:\n" << "- QNetworkConnectionMonitor tests:\n"
<< indent << "r" << indent << "Test detection of disruption of route to target.\n" << indent << "r" << indent << "Test detection of disruption of route to target.\n"
<< "- General\n" << "- General\n"
@ -70,9 +68,6 @@ int main(int argc, char *argv[])
}; };
switch (getCommand()) { switch (getCommand()) {
case 'c':
testDetectDisconnection();
break;
case 'r': case 'r':
testDetectRouteDisrupted(); testDetectRouteDisrupted();
break; break;
@ -84,16 +79,19 @@ int main(int argc, char *argv[])
return 0; return 0;
} }
bool ensureNetworkAccessible(QNetworkStatusMonitor &status, QTextStream &writer) bool ensureNetworkAccessible(QTextStream &writer)
{ {
if (!status.isNetworkAccessible()) { auto netInfo = QNetworkInformation::instance();
if (netInfo->reachability() == QNetworkInformation::Reachability::Disconnected) {
writer << "Network currently not accessible, please make sure you have an internet " writer << "Network currently not accessible, please make sure you have an internet "
"connection. Will wait for a connection for 20 seconds.\n"; "connection. Will wait for a connection for 20 seconds.\n";
writer.flush(); writer.flush();
QDeadlineTimer timer{ 20 * 1000 }; QDeadlineTimer timer{ 20 * 1000 };
while (!timer.hasExpired() && !status.isNetworkAccessible()) while (!timer.hasExpired()
&& netInfo->reachability() == QNetworkInformation::Reachability::Disconnected) {
QCoreApplication::processEvents(); QCoreApplication::processEvents();
if (!status.isNetworkAccessible()) { }
if (netInfo->reachability() == QNetworkInformation::Reachability::Disconnected) {
writer << "Error: No network in 20 seconds, ending now!\n"; writer << "Error: No network in 20 seconds, ending now!\n";
return false; return false;
} }
@ -102,53 +100,16 @@ bool ensureNetworkAccessible(QNetworkStatusMonitor &status, QTextStream &writer)
return true; return true;
} }
void testDetectDisconnection()
{
QTextStream writer(stdout);
QNetworkStatusMonitor status;
if (!status.start()) {
writer << "Error: Failed to start";
return;
}
if (!ensureNetworkAccessible(status, writer))
return;
QSignalSpy onlineStateSpy(&status, &QNetworkStatusMonitor::onlineStateChanged);
writer << "Please disconnect from the internet within 20 seconds\n";
writer.flush();
QDeadlineTimer timer{ 20 * 1000 };
while (!timer.hasExpired() && status.isNetworkAccessible())
QCoreApplication::processEvents();
if (status.isNetworkAccessible()) {
writer << "Error: Still connected after 20 seconds, ending now!\n";
return;
}
if (onlineStateSpy.count() == 0) {
writer << "Error: There was a disconnection but there was no signal emitted!\n";
return;
}
// Get the final parameter of the final signal emission and make sure it is false.
if (onlineStateSpy.last().last().toBool()) {
writer << "Error: There was a disconnection but the latest signal emitted says we are online!\n";
return;
}
writer << "Success, connection loss was detected!\n";
}
void testDetectRouteDisrupted() void testDetectRouteDisrupted()
{ {
QTextStream writer(stdout); QTextStream writer(stdout);
{ {
QNetworkStatusMonitor status; if (!QNetworkInformation::load()) {
if (!status.start()) {
writer << "Error: Failed to start"; writer << "Error: Failed to start";
return; return;
} }
if (!ensureNetworkAccessible(status, writer)) if (!ensureNetworkAccessible(writer))
return; return;
} }