QNI: Add API to check if connection is metered

This may be a useful factor in deciding whether or not you should
perform communications over the network which are not purely essential.
For example, if you have a logging mechanism you can delay uploading
them until you are no longer on a metered network.

Task-number: QTBUG-91024
Change-Id: I19d32f031a3893512dc440914133678004987fb1
Reviewed-by: Paul Wicking <paul.wicking@qt.io>
Reviewed-by: Marc Mutz <marc.mutz@qt.io>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
This commit is contained in:
Mårten Nordheim 2021-10-25 20:23:24 +02:00
parent 2148e1f0e6
commit bd52c1bba6
6 changed files with 89 additions and 6 deletions

View File

@ -457,6 +457,11 @@ QNetworkInformationBackendFactory::~QNetworkInformationBackendFactory()
property will provide useful results. Otherwise it will always return
\c{TransportMedium::Unknown}.
See also QNetworkInformation::TransportMedium.
\value Metered
If the plugin supports this feature then the \c isMetered
property will provide useful results. Otherwise it will always return
\c{false}.
*/
/*!
@ -520,6 +525,8 @@ QNetworkInformation::QNetworkInformation(QNetworkInformationBackend *backend)
&QNetworkInformation::isBehindCaptivePortalChanged);
connect(backend, &QNetworkInformationBackend::transportMediumChanged, this,
&QNetworkInformation::transportMediumChanged);
connect(backend, &QNetworkInformationBackend::isMeteredChanged, this,
&QNetworkInformation::isMeteredChanged);
}
/*!
@ -579,6 +586,22 @@ QNetworkInformation::TransportMedium QNetworkInformation::transportMedium() cons
return d_func()->backend->transportMedium();
}
/*!
\property QNetworkInformation::isMetered
\brief Check if the current connection is metered
\since 6.3
This property returns whether the current connection is (known to be)
metered or not. You can use this as a guiding factor to decide whether your
application should perform certain network requests or uploads.
For instance, you may not want to upload logs or diagnostics while this
property is \c true.
*/
bool QNetworkInformation::isMetered() const
{
return d_func()->backend->isMetered();
}
/*!
Returns the name of the currently loaded backend.
*/

View File

@ -58,6 +58,7 @@ class Q_NETWORK_EXPORT QNetworkInformation : public QObject
Q_PROPERTY(bool isBehindCaptivePortal READ isBehindCaptivePortal
NOTIFY isBehindCaptivePortalChanged)
Q_PROPERTY(TransportMedium transportMedium READ transportMedium NOTIFY transportMediumChanged)
Q_PROPERTY(bool isMetered READ isMetered NOTIFY isMeteredChanged)
public:
enum class Reachability {
Unknown,
@ -81,6 +82,7 @@ public:
Reachability = 0x1,
CaptivePortal = 0x2,
TransportMedium = 0x4,
Metered = 0x8,
};
Q_DECLARE_FLAGS(Features, Feature)
Q_FLAG(Features)
@ -91,6 +93,8 @@ public:
TransportMedium transportMedium() const;
bool isMetered() const;
QString backendName() const;
bool supports(Features features) const;
@ -106,6 +110,7 @@ Q_SIGNALS:
void reachabilityChanged(Reachability newReachability);
void isBehindCaptivePortalChanged(bool state);
void transportMediumChanged(TransportMedium current);
void isMeteredChanged(bool isMetered);
private:
friend struct QNetworkInformationDeleter;

View File

@ -87,11 +87,13 @@ public:
Reachability reachability() const { return m_reachability; }
bool behindCaptivePortal() const { return m_behindCaptivePortal; }
TransportMedium transportMedium() const { return m_transportMedium; }
bool isMetered() const { return m_metered; }
Q_SIGNALS:
void reachabilityChanged(Reachability reachability);
void behindCaptivePortalChanged(bool behindPortal);
void transportMediumChanged(TransportMedium medium);
void isMeteredChanged(bool isMetered);
protected:
void setReachability(QNetworkInformation::Reachability reachability)
@ -118,10 +120,19 @@ protected:
}
}
void setMetered(bool isMetered)
{
if (m_metered != isMetered) {
m_metered = isMetered;
emit isMeteredChanged(isMetered);
}
}
private:
Reachability m_reachability = Reachability::Unknown;
TransportMedium m_transportMedium = TransportMedium::Unknown;
bool m_behindCaptivePortal = false;
bool m_metered = false;
Q_DISABLE_COPY_MOVE(QNetworkInformationBackend)
friend class QNetworkInformation;

View File

@ -29,6 +29,7 @@
#include <QtNetwork/private/qnetworkinformation_p.h>
#include <QtNetwork/qnetworkinformation.h>
#include <QtTest/qtest.h>
#include <QtTest/qsignalspy.h>
#include <limits>
#include <memory>
@ -43,6 +44,7 @@ private slots:
void reachability();
void behindCaptivePortal();
void transportMedium();
void isMetered();
void cleanupTestCase();
private:
@ -88,11 +90,18 @@ public:
instance->setTransportMedium(medium);
}
static void setNewMetered(bool metered)
{
Q_ASSERT(instance);
instance->setMetered(metered);
}
static QNetworkInformation::Features featuresSupportedStatic()
{
return { QNetworkInformation::Feature::Reachability
| QNetworkInformation::Feature::CaptivePortal
| QNetworkInformation::Feature::TransportMedium };
| QNetworkInformation::Feature::TransportMedium
| QNetworkInformation::Feature::Metered };
}
private:
@ -144,9 +153,10 @@ void tst_QNetworkInformation::supportedFeatures()
{
auto info = QNetworkInformation::instance();
auto allFeatures = QNetworkInformation::Features(
QNetworkInformation::Feature::CaptivePortal | QNetworkInformation::Feature::Reachability
| QNetworkInformation::Feature::TransportMedium);
auto allFeatures = QNetworkInformation::Features(QNetworkInformation::Feature::CaptivePortal
| QNetworkInformation::Feature::Reachability
| QNetworkInformation::Feature::TransportMedium
| QNetworkInformation::Feature::Metered);
QCOMPARE(info->supportedFeatures(), allFeatures);
@ -154,6 +164,7 @@ void tst_QNetworkInformation::supportedFeatures()
QVERIFY(info->supports(QNetworkInformation::Feature::CaptivePortal));
QVERIFY(info->supports(QNetworkInformation::Feature::Reachability));
QVERIFY(info->supports(QNetworkInformation::Feature::TransportMedium));
QVERIFY(info->supports(QNetworkInformation::Feature::Metered));
}
void tst_QNetworkInformation::reachability()
@ -248,5 +259,22 @@ void tst_QNetworkInformation::transportMedium()
QVERIFY(!signalEmitted);
}
void tst_QNetworkInformation::isMetered()
{
auto info = QNetworkInformation::instance();
QSignalSpy spy(info, &QNetworkInformation::isMeteredChanged);
QVERIFY(!info->isMetered());
MockBackend::setNewMetered(true);
QCOMPARE(spy.count(), 1);
QVERIFY(info->isMetered());
QVERIFY(spy[0][0].toBool());
spy.clear();
// Set the same value again, signal should not be emitted again
MockBackend::setNewMetered(true);
QCOMPARE(spy.count(), 0);
}
QTEST_MAIN(tst_QNetworkInformation);
#include "tst_qnetworkinformation.moc"

View File

@ -74,13 +74,20 @@ public slots:
updateText();
}
void updateMetered(bool newValue)
{
metered = newValue;
updateText();
}
private:
void updateText()
{
QString str =
QLatin1String("Reachability: %1\nBehind captive portal: %2\nTransport medium: %3")
QLatin1String("Reachability: %1\nBehind captive portal: %2\nTransport medium: %3"
"\nMetered: %4")
.arg(enumToString(reachability), QStringView(captive ? u"true" : u"false"),
enumToString(transportMedium));
enumToString(transportMedium), metered ? u"true" : u"false");
label->setText(str);
}
@ -88,6 +95,7 @@ private:
Reachability reachability = Reachability::Unknown;
TransportMedium transportMedium = TransportMedium::Unknown;
bool captive = false;
bool metered = false;
};
#endif

View File

@ -70,6 +70,11 @@ int main(int argc, char **argv)
qDebug() << "Updated, current transport medium:" << newMedium;
});
QObject::connect(info, &QNetworkInformation::isMeteredChanged,
[](bool metered) {
qDebug() << "Updated, metered:" << metered;
});
#ifdef MOBILE
// Some extra connections to update the window if we're on mobile
QObject::connect(info, &QNetworkInformation::reachabilityChanged, &window,
@ -78,11 +83,14 @@ int main(int argc, char **argv)
&MainWindow::updateCaptiveState);
QObject::connect(info, &QNetworkInformation::transportMediumChanged, &window,
&MainWindow::updateTransportMedium);
QObject::connect(info, &QNetworkInformation::isMeteredChanged, &window,
&MainWindow::updateMetered);
#endif
qDebug() << "Initial reachability:" << info->reachability();
qDebug() << "Behind captive portal:" << info->isBehindCaptivePortal();
qDebug() << "Transport medium:" << info->transportMedium();
qDebug() << "Is metered:" << info->isMetered();
return app.exec();
}