Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qapplenetworkinformationbackend.mm
Go to the documentation of this file.
1// Copyright (C) 2024 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#include <QtNetwork/private/qnetworkinformation_p.h>
6
7#include <QtCore/qglobal.h>
8#include <QtCore/private/qobject_p.h>
9#include <QtCore/qmutex.h>
10
11#include <Network/Network.h>
12
13QT_BEGIN_NAMESPACE
14Q_DECLARE_LOGGING_CATEGORY(lcNetInfoSCR)
15Q_LOGGING_CATEGORY(lcNetInfoSCR, "qt.network.info.applenetworkinfo");
16
17namespace {
18
19class ReachabilityDispatchQueue
20{
21public:
22 ReachabilityDispatchQueue()
23 {
24 queue = dispatch_queue_create("qt-network-reachability-queue", nullptr);
25 if (!queue)
26 qCWarning(lcNetInfoSCR, "Failed to create a dispatch queue for reachability probes");
27 }
28
29 ~ReachabilityDispatchQueue()
30 {
31 if (queue)
32 dispatch_release(queue);
33 }
34
35 dispatch_queue_t data() const
36 {
37 return queue;
38 }
39
40private:
41 dispatch_queue_t queue = nullptr;
42
43 Q_DISABLE_COPY_MOVE(ReachabilityDispatchQueue)
44};
45
46dispatch_queue_t qt_reachability_queue()
47{
48 static const ReachabilityDispatchQueue reachabilityQueue;
49 return reachabilityQueue.data();
50}
51
52} // unnamed namespace
53
55{
56 return QString::fromUtf16(QNetworkInformationBackend::PluginNames
57 [QNetworkInformationBackend::PluginNamesAppleIndex]);
58}
59
61{
63public:
70 Q_ENUM(InterfaceType)
71
74
75 QString name() const override { return backendName(); }
77 {
78 return featuresSupportedStatic();
79 }
80
82 {
83 return QNetworkInformation::Features(QNetworkInformation::Feature::Reachability
84 | QNetworkInformation::Feature::TransportMedium);
85 }
86
87 void reachabilityChanged(bool isOnline);
88 void interfaceTypeChanged(QAppleNetworkInformationBackend::InterfaceType type);
89
90private:
92
93 bool isReachable() const;
94 bool startMonitoring();
95 void stopMonitoring();
96 void updateState(nw_path_t state);
97
98 nw_path_status_t status = nw_path_status_invalid;
99
100 struct QueueCallbackData
101 {
102 QMutex monitorMutex;
103 QAppleNetworkInformationBackend *backend = nullptr;
104 } *callbackData = nullptr;
105
106 nw_path_monitor_t monitor = nullptr;
107 QAppleNetworkInformationBackend::InterfaceType interface = InterfaceType::Unknown;
108};
109
111{
112 Q_OBJECT
113 Q_PLUGIN_METADATA(IID QNetworkInformationBackendFactory_iid)
115public:
118 QString name() const override { return backendName(); }
120 {
121 return QAppleNetworkInformationBackend::featuresSupportedStatic();
122 }
123
125 QNetworkInformation::Features requiredFeatures) const override
126 {
127 if ((requiredFeatures & featuresSupported()) != requiredFeatures)
128 return nullptr;
130 }
131
132private:
134};
135
136QAppleNetworkInformationBackend::QAppleNetworkInformationBackend()
137{
138 startMonitoring();
139}
140
145
147{
148 setReachability(isOnline ? QNetworkInformation::Reachability::Online
149 : QNetworkInformation::Reachability::Disconnected);
150}
151
152void QAppleNetworkInformationBackend::interfaceTypeChanged(QAppleNetworkInformationBackend::InterfaceType type)
153{
154
155 if (reachability() == QNetworkInformation::Reachability::Disconnected) {
156 setTransportMedium(QNetworkInformation::TransportMedium::Unknown);
157 } else {
158 switch (type) {
159 case QAppleNetworkInformationBackend::InterfaceType::Ethernet:
160 setTransportMedium(QNetworkInformation::TransportMedium::Ethernet);
161 break;
162 case QAppleNetworkInformationBackend::InterfaceType::Cellular:
163 setTransportMedium(QNetworkInformation::TransportMedium::Cellular);
164 break;
165 case QAppleNetworkInformationBackend::InterfaceType::WiFi:
166 setTransportMedium(QNetworkInformation::TransportMedium::WiFi);
167 break;
168 case QAppleNetworkInformationBackend::InterfaceType::Unknown:
169 setTransportMedium(QNetworkInformation::TransportMedium::Unknown);
170 break;
171 }
172 }
173}
174
175bool QAppleNetworkInformationBackend::isReachable() const
176{
177 return status == nw_path_status_satisfied;
178}
179
180bool QAppleNetworkInformationBackend::startMonitoring()
181{
182 Q_ASSERT(!monitor && !callbackData);
183
184 monitor = nw_path_monitor_create();
185 if (monitor == nullptr) {
186 qCWarning(lcNetInfoSCR, "Failed to create a path monitor, cannot determine current reachability.");
187 return false;
188 }
189
190 auto queue = qt_reachability_queue();
191 if (!queue) {
192 qCWarning(lcNetInfoSCR, "Failed to create a dispatch queue to schedule a probe on");
193 nw_release(monitor);
194 monitor = nullptr;
195 return false;
196 }
197
198 callbackData = new QueueCallbackData;
199 auto *data = callbackData;
200 callbackData->backend = this;
201
202 nw_path_monitor_set_update_handler(monitor, [data](nw_path_t path){
203 const QMutexLocker lock(&data->monitorMutex);
204 if (data->backend)
205 data->backend->updateState(path);
206 // Else - we were cancelled and will delete 'data' in the callback below.
207 // Presumably, this gets never called after 'cancel handler'.
208 });
209
210 nw_path_monitor_set_cancel_handler(monitor, [data]{
211 delete data;
212 });
213
214 nw_path_monitor_set_queue(monitor, queue);
215 nw_path_monitor_start(monitor);
216 return true;
217}
218
219void QAppleNetworkInformationBackend::stopMonitoring()
220{
221 Q_ASSERT(callbackData && monitor);
222 {
223 const QMutexLocker lock(&callbackData->monitorMutex); // Release the lock _before_ cancelling.
224 callbackData->backend = nullptr; // This will prevent updateState calls from the queue.
225 callbackData = nullptr; // To be deleted in the cancellation callback.
226 }
227 nw_path_monitor_cancel(monitor);
228 nw_release(monitor);
229 monitor = nullptr;
230}
231
232void QAppleNetworkInformationBackend::updateState(nw_path_t state)
233{
234 Q_ASSERT(callbackData);
235
236 // Lock is acquired in the callback (which is calling us).
237
238 // NETMONTODO: for now, 'online' for us means nw_path_status_satisfied
239 // is set. There are more possible flags that require more tests/some special
240 // setup. So in future this part and related can change/be extended.
241 const bool wasReachable = isReachable();
242 const QAppleNetworkInformationBackend::InterfaceType hadInterfaceType = interface;
243 const nw_path_status_t previousStatus = status;
244
245 status = nw_path_get_status(state);
246 if (wasReachable != isReachable() || previousStatus == nw_path_status_invalid)
247 reachabilityChanged(isReachable());
248
249 nw_path_enumerate_interfaces(state, ^(nw_interface_t nwInterface) {
250 if (nw_path_uses_interface_type(state, nw_interface_get_type(nwInterface))) {
251 const nw_interface_type_t type = nw_interface_get_type(nwInterface);
252
253 switch (type) {
254 case nw_interface_type_wifi:
255 interface = QAppleNetworkInformationBackend::InterfaceType::WiFi;
256 break;
257 case nw_interface_type_cellular:
258 interface = QAppleNetworkInformationBackend::InterfaceType::Cellular;
259 break;
260 case nw_interface_type_wired:
261 interface = QAppleNetworkInformationBackend::InterfaceType::Ethernet;
262 break;
263 default:
264 interface = QAppleNetworkInformationBackend::InterfaceType::Unknown;
265 break;
266 }
267
268 return false;
269 }
270
271 return true;
272 });
273
274 if (hadInterfaceType != interface)
275 interfaceTypeChanged(interface);
276}
277
278QT_END_NAMESPACE
279
280#include "qapplenetworkinformationbackend.moc"
QString name() const override
Backend name, return the same in QNetworkInformationBackend::name().
QNetworkInformation::Features featuresSupported() const override
Features supported, return the same in QNetworkInformationBackend::featuresSupported().
QNetworkInformationBackend * create(QNetworkInformation::Features requiredFeatures) const override
Create and return an instance of QNetworkInformationBackend.
void interfaceTypeChanged(QAppleNetworkInformationBackend::InterfaceType type)
QNetworkInformation::Features featuresSupported() const override
Features supported, return the same in QNetworkInformationBackendFactory::featuresSupported().
QString name() const override
Backend name, return the same in QNetworkInformationBackendFactory::name().
static QNetworkInformation::Features featuresSupportedStatic()
static QString backendName()