1 | // Copyright (C) 2021 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 | |
4 | // #define DEBUG_LOADING |
5 | |
6 | #include "qnetworkinformation.h" |
7 | #include <QtNetwork/private/qnetworkinformation_p.h> |
8 | #include <QtNetwork/qnetworkinformation.h> |
9 | |
10 | #include <QtCore/private/qobject_p.h> |
11 | #include <QtCore/qcoreapplication.h> |
12 | #include <QtCore/qmutex.h> |
13 | #include <QtCore/qthread.h> |
14 | #include <QtCore/private/qfactoryloader_p.h> |
15 | |
16 | #include <algorithm> |
17 | #include <memory> |
18 | #include <mutex> |
19 | |
20 | QT_BEGIN_NAMESPACE |
21 | Q_DECLARE_LOGGING_CATEGORY(lcNetInfo) |
22 | Q_LOGGING_CATEGORY(lcNetInfo, "qt.network.info" ); |
23 | |
24 | struct QNetworkInformationDeleter |
25 | { |
26 | void operator()(QNetworkInformation *information) { delete information; } |
27 | }; |
28 | |
29 | Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, qniLoader, |
30 | (QNetworkInformationBackendFactory_iid, |
31 | QStringLiteral("/networkinformation" ))) |
32 | |
33 | struct QStaticNetworkInformationDataHolder |
34 | { |
35 | QMutex instanceMutex; |
36 | std::unique_ptr<QNetworkInformation, QNetworkInformationDeleter> instanceHolder; |
37 | QList<QNetworkInformationBackendFactory *> factories; |
38 | }; |
39 | Q_GLOBAL_STATIC(QStaticNetworkInformationDataHolder, dataHolder); |
40 | |
41 | static void networkInfoCleanup() |
42 | { |
43 | if (!dataHolder.exists()) |
44 | return; |
45 | QMutexLocker locker(&dataHolder->instanceMutex); |
46 | QNetworkInformation *instance = dataHolder->instanceHolder.get(); |
47 | if (!instance) |
48 | return; |
49 | |
50 | auto needsReinvoke = instance->thread() && instance->thread() != QThread::currentThread(); |
51 | if (needsReinvoke) { |
52 | QMetaObject::invokeMethod(object: dataHolder->instanceHolder.get(), function: []() { networkInfoCleanup(); }); |
53 | return; |
54 | } |
55 | dataHolder->instanceHolder.reset(); |
56 | } |
57 | |
58 | class QNetworkInformationPrivate : public QObjectPrivate |
59 | { |
60 | Q_DECLARE_PUBLIC(QNetworkInformation) |
61 | public: |
62 | QNetworkInformationPrivate(QNetworkInformationBackend *backend) : backend(backend) { |
63 | qAddPostRoutine(&networkInfoCleanup); |
64 | } |
65 | |
66 | static QNetworkInformation *create(QNetworkInformation::Features features); |
67 | static QNetworkInformation *create(QStringView name); |
68 | static QNetworkInformation *instance() |
69 | { |
70 | if (!dataHolder()) |
71 | return nullptr; |
72 | QMutexLocker locker(&dataHolder->instanceMutex); |
73 | return dataHolder->instanceHolder.get(); |
74 | } |
75 | static QStringList backendNames(); |
76 | static void addToList(QNetworkInformationBackendFactory *factory); |
77 | static void removeFromList(QNetworkInformationBackendFactory *factory); |
78 | |
79 | private: |
80 | static bool initializeList(); |
81 | |
82 | std::unique_ptr<QNetworkInformationBackend> backend; |
83 | }; |
84 | |
85 | bool QNetworkInformationPrivate::initializeList() |
86 | { |
87 | if (!qniLoader()) |
88 | return false; |
89 | if (!dataHolder()) |
90 | return false; |
91 | Q_CONSTINIT static QBasicMutex mutex; |
92 | QMutexLocker initLocker(&mutex); |
93 | |
94 | #if QT_CONFIG(library) |
95 | qniLoader->update(); |
96 | #endif |
97 | // Instantiates the plugins (and registers the factories) |
98 | int index = 0; |
99 | while (qniLoader->instance(index)) |
100 | ++index; |
101 | initLocker.unlock(); |
102 | |
103 | // Now sort the list on number of features available (then name) |
104 | const auto featuresNameOrder = [](QNetworkInformationBackendFactory *a, |
105 | QNetworkInformationBackendFactory *b) { |
106 | if (!a || !b) |
107 | return a && !b; |
108 | auto aFeaturesSupported = qPopulationCount(v: unsigned(a->featuresSupported())); |
109 | auto bFeaturesSupported = qPopulationCount(v: unsigned(b->featuresSupported())); |
110 | return aFeaturesSupported > bFeaturesSupported |
111 | || (aFeaturesSupported == bFeaturesSupported |
112 | && a->name().compare(s: b->name(), cs: Qt::CaseInsensitive) < 0); |
113 | }; |
114 | QMutexLocker instanceLocker(&dataHolder->instanceMutex); |
115 | std::sort(first: dataHolder->factories.begin(), last: dataHolder->factories.end(), comp: featuresNameOrder); |
116 | |
117 | return !dataHolder->factories.isEmpty(); |
118 | } |
119 | |
120 | void QNetworkInformationPrivate::addToList(QNetworkInformationBackendFactory *factory) |
121 | { |
122 | // @note: factory is in the base class ctor |
123 | if (!dataHolder()) |
124 | return; |
125 | QMutexLocker locker(&dataHolder->instanceMutex); |
126 | dataHolder->factories.append(t: factory); |
127 | } |
128 | |
129 | void QNetworkInformationPrivate::removeFromList(QNetworkInformationBackendFactory *factory) |
130 | { |
131 | // @note: factory is in the base class dtor |
132 | if (!dataHolder.exists()) |
133 | return; |
134 | QMutexLocker locker(&dataHolder->instanceMutex); |
135 | dataHolder->factories.removeAll(t: factory); |
136 | } |
137 | |
138 | QStringList QNetworkInformationPrivate::backendNames() |
139 | { |
140 | if (!dataHolder()) |
141 | return {}; |
142 | if (!initializeList()) |
143 | return {}; |
144 | |
145 | QMutexLocker locker(&dataHolder->instanceMutex); |
146 | const QList copy = dataHolder->factories; |
147 | locker.unlock(); |
148 | |
149 | QStringList result; |
150 | result.reserve(asize: copy.size()); |
151 | for (const auto *factory : copy) |
152 | result << factory->name(); |
153 | return result; |
154 | } |
155 | |
156 | QNetworkInformation *QNetworkInformationPrivate::create(QStringView name) |
157 | { |
158 | if (name.isEmpty()) |
159 | return nullptr; |
160 | if (!dataHolder()) |
161 | return nullptr; |
162 | #ifdef DEBUG_LOADING |
163 | qDebug().nospace() << "create() called with name=\"" << name |
164 | << "\". instanceHolder initialized? " << !!dataHolder->instanceHolder; |
165 | #endif |
166 | if (!initializeList()) { |
167 | #ifdef DEBUG_LOADING |
168 | qDebug("Failed to initialize list, returning." ); |
169 | #endif |
170 | return nullptr; |
171 | } |
172 | |
173 | QMutexLocker locker(&dataHolder->instanceMutex); |
174 | if (dataHolder->instanceHolder) |
175 | return dataHolder->instanceHolder.get(); |
176 | |
177 | |
178 | const auto nameMatches = [name](QNetworkInformationBackendFactory *factory) { |
179 | return factory->name().compare(s: name, cs: Qt::CaseInsensitive) == 0; |
180 | }; |
181 | auto it = std::find_if(first: dataHolder->factories.cbegin(), last: dataHolder->factories.cend(), |
182 | pred: nameMatches); |
183 | if (it == dataHolder->factories.cend()) { |
184 | #ifdef DEBUG_LOADING |
185 | if (dataHolder->factories.isEmpty()) { |
186 | qDebug("No plugins available" ); |
187 | } else { |
188 | QString listNames; |
189 | listNames.reserve(8 * dataHolder->factories.count()); |
190 | for (const auto *factory : std::as_const(dataHolder->factories)) |
191 | listNames += factory->name() + QStringLiteral(", " ); |
192 | listNames.chop(2); |
193 | qDebug().nospace() << "Couldn't find " << name << " in list with names: { " |
194 | << listNames << " }" ; |
195 | } |
196 | #endif |
197 | return nullptr; |
198 | } |
199 | #ifdef DEBUG_LOADING |
200 | qDebug() << "Creating instance using loader named " << (*it)->name(); |
201 | #endif |
202 | QNetworkInformationBackend *backend = (*it)->create(requiredFeatures: (*it)->featuresSupported()); |
203 | if (!backend) |
204 | return nullptr; |
205 | dataHolder->instanceHolder.reset(p: new QNetworkInformation(backend)); |
206 | Q_ASSERT(name.isEmpty() |
207 | || dataHolder->instanceHolder->backendName().compare(name, Qt::CaseInsensitive) == 0); |
208 | return dataHolder->instanceHolder.get(); |
209 | } |
210 | |
211 | QNetworkInformation *QNetworkInformationPrivate::create(QNetworkInformation::Features features) |
212 | { |
213 | if (!dataHolder()) |
214 | return nullptr; |
215 | #ifdef DEBUG_LOADING |
216 | qDebug().nospace() << "create() called with features=\"" << features |
217 | << "\". instanceHolder initialized? " << !!dataHolder->instanceHolder; |
218 | #endif |
219 | if (features == 0) |
220 | return nullptr; |
221 | |
222 | if (!initializeList()) { |
223 | #ifdef DEBUG_LOADING |
224 | qDebug("Failed to initialize list, returning." ); |
225 | #endif |
226 | return nullptr; |
227 | } |
228 | QMutexLocker locker(&dataHolder->instanceMutex); |
229 | if (dataHolder->instanceHolder) |
230 | return dataHolder->instanceHolder.get(); |
231 | |
232 | const auto supportsRequestedFeatures = [features](QNetworkInformationBackendFactory *factory) { |
233 | return factory && (factory->featuresSupported() & features) == features; |
234 | }; |
235 | |
236 | for (auto it = dataHolder->factories.cbegin(), end = dataHolder->factories.cend(); it != end; |
237 | ++it) { |
238 | it = std::find_if(first: it, last: end, pred: supportsRequestedFeatures); |
239 | if (it == end) { |
240 | #ifdef DEBUG_LOADING |
241 | if (dataHolder->factories.isEmpty()) { |
242 | qDebug("No plugins available" ); |
243 | } else { |
244 | QStringList names; |
245 | names.reserve(dataHolder->factories.count()); |
246 | for (const auto *factory : std::as_const(dataHolder->factories)) |
247 | names += factory->name(); |
248 | qDebug() << "None of the following backends has all the requested features:" |
249 | << names << features; |
250 | } |
251 | #endif |
252 | break; |
253 | } |
254 | #ifdef DEBUG_LOADING |
255 | qDebug() << "Creating instance using loader named" << (*it)->name(); |
256 | #endif |
257 | if (QNetworkInformationBackend *backend = (*it)->create(requiredFeatures: features)) { |
258 | dataHolder->instanceHolder.reset(p: new QNetworkInformation(backend)); |
259 | Q_ASSERT(dataHolder->instanceHolder->supports(features)); |
260 | return dataHolder->instanceHolder.get(); |
261 | } |
262 | #ifdef DEBUG_LOADING |
263 | else { |
264 | qDebug() << "The factory returned a nullptr" ; |
265 | } |
266 | #endif |
267 | } |
268 | #ifdef DEBUG_LOADING |
269 | qDebug() << "Couldn't find/create an appropriate backend." ; |
270 | #endif |
271 | return nullptr; |
272 | } |
273 | |
274 | /*! |
275 | \class QNetworkInformationBackend |
276 | \internal (Semi-private) |
277 | \brief QNetworkInformationBackend provides the interface with |
278 | which QNetworkInformation does all of its actual work. |
279 | |
280 | Deriving from and implementing this class makes it a candidate |
281 | for use with QNetworkInformation. The derived class must, on |
282 | updates, call setters in the QNetworkInformationBackend which |
283 | will update the values and emit signals if the value has changed. |
284 | |
285 | \sa QNetworkInformationBackendFactory |
286 | */ |
287 | |
288 | /*! |
289 | \internal |
290 | Destroys base backend class. |
291 | */ |
292 | QNetworkInformationBackend::~QNetworkInformationBackend() = default; |
293 | |
294 | /*! |
295 | \fn QNetworkInformationBackend::name() |
296 | |
297 | Backend name, return the same in |
298 | QNetworkInformationBackendFactory::name(). |
299 | */ |
300 | |
301 | /*! |
302 | \fn QNetworkInformation::Features QNetworkInformationBackend::featuresSupported() |
303 | |
304 | Features supported, return the same in |
305 | QNetworkInformationBackendFactory::featuresSupported(). |
306 | */ |
307 | |
308 | /*! |
309 | \fn void QNetworkInformationBackend::reachabilityChanged() |
310 | |
311 | You should not emit this signal manually, call setReachability() |
312 | instead which will emit this signal when the value changes. |
313 | |
314 | \sa setReachability |
315 | */ |
316 | |
317 | /*! |
318 | \fn void QNetworkInformationBackend::setReachability(QNetworkInformation::Reachability reachability) |
319 | |
320 | Call this when reachability has changed. It will automatically |
321 | emit reachabilityChanged(). |
322 | |
323 | \sa setReachability |
324 | */ |
325 | |
326 | /*! |
327 | \class QNetworkInformationBackendFactory |
328 | \internal (Semi-private) |
329 | \brief QNetworkInformationBackendFactory provides the interface |
330 | for creating instances of QNetworkInformationBackend. |
331 | |
332 | Deriving from and implementing this class will let you register |
333 | your plugin with QNetworkInformation. It must provide some basic |
334 | information for querying information about the backend, and must |
335 | also create the backend if requested. If some pre-conditions for |
336 | the backend is not met it must return \nullptr. |
337 | */ |
338 | |
339 | /*! |
340 | \internal |
341 | Adds the factory to an internal list. |
342 | */ |
343 | QNetworkInformationBackendFactory::QNetworkInformationBackendFactory() |
344 | { |
345 | QNetworkInformationPrivate::addToList(factory: this); |
346 | } |
347 | |
348 | /*! |
349 | \internal |
350 | Removes the factory from an internal list. |
351 | */ |
352 | QNetworkInformationBackendFactory::~QNetworkInformationBackendFactory() |
353 | { |
354 | QNetworkInformationPrivate::removeFromList(factory: this); |
355 | } |
356 | |
357 | /*! |
358 | \fn QString QNetworkInformationBackendFactory::name() |
359 | |
360 | Backend name, return the same in |
361 | QNetworkInformationBackend::name(). |
362 | */ |
363 | |
364 | /*! |
365 | \fn QNetworkInformation::Features QNetworkInformationBackendFactory::featuresSupported() |
366 | |
367 | Features supported, return the same in |
368 | QNetworkInformationBackend::featuresSupported(). |
369 | The factory should not promise support for features that wouldn't |
370 | be available after creating the backend. |
371 | */ |
372 | |
373 | /*! |
374 | \fn QNetworkInformationBackend *QNetworkInformationBackendFactory::create() |
375 | |
376 | Create and return an instance of QNetworkInformationBackend. It |
377 | will be deallocated by QNetworkInformation on shutdown. If some |
378 | precondition is not met, meaning the backend would not function |
379 | correctly, then you must return \nullptr. |
380 | */ |
381 | |
382 | /*! |
383 | \class QNetworkInformation |
384 | \inmodule QtNetwork |
385 | \since 6.1 |
386 | \brief QNetworkInformation exposes various network information |
387 | through native backends. |
388 | |
389 | QNetworkInformation provides a cross-platform interface to |
390 | network-related information through plugins. |
391 | |
392 | Various plugins can have various functionality supported, and so |
393 | you can load() plugins based on which features are needed. |
394 | |
395 | QNetworkInformation is a singleton and stays alive from the first |
396 | successful load() until destruction of the QCoreApplication object. |
397 | If you destroy and re-create the QCoreApplication object you must call |
398 | load() again. |
399 | |
400 | \sa QNetworkInformation::Feature |
401 | */ |
402 | |
403 | /*! |
404 | \enum QNetworkInformation::Feature |
405 | |
406 | Lists all of the features that a plugin may currently support. |
407 | This can be used in QNetworkInformation::load(). |
408 | |
409 | \value Reachability |
410 | If the plugin supports this feature then the \c reachability property |
411 | will provide useful results. Otherwise it will always return |
412 | \c{Reachability::Unknown}. |
413 | See also QNetworkInformation::Reachability. |
414 | |
415 | \value CaptivePortal |
416 | If the plugin supports this feature then the \c isBehindCaptivePortal |
417 | property will provide useful results. Otherwise it will always return |
418 | \c{false}. |
419 | |
420 | \value TransportMedium |
421 | If the plugin supports this feature then the \c transportMedium |
422 | property will provide useful results. Otherwise it will always return |
423 | \c{TransportMedium::Unknown}. |
424 | See also QNetworkInformation::TransportMedium. |
425 | |
426 | \value Metered |
427 | If the plugin supports this feature then the \c isMetered |
428 | property will provide useful results. Otherwise it will always return |
429 | \c{false}. |
430 | */ |
431 | |
432 | /*! |
433 | \enum QNetworkInformation::Reachability |
434 | |
435 | \value Unknown |
436 | If this value is returned then we may be connected but the OS |
437 | has still not confirmed full connectivity, or this feature |
438 | is not supported. |
439 | \value Disconnected |
440 | Indicates that the system may have no connectivity at all. |
441 | \value Local |
442 | Indicates that the system is connected to a network, but it |
443 | might only be able to access devices on the local network. |
444 | \value Site |
445 | Indicates that the system is connected to a network, but it |
446 | might only be able to access devices on the local subnet or an |
447 | intranet. |
448 | \value Online |
449 | Indicates that the system is connected to a network and |
450 | able to access the Internet. |
451 | |
452 | \sa QNetworkInformation::reachability |
453 | */ |
454 | |
455 | /*! |
456 | \enum QNetworkInformation::TransportMedium |
457 | \since 6.3 |
458 | |
459 | Lists the currently recognized media with which one can connect to the |
460 | internet. |
461 | |
462 | \value Unknown |
463 | Returned if either the OS reports no active medium, the active medium is |
464 | not recognized by Qt, or the TransportMedium feature is not supported. |
465 | \value Ethernet |
466 | Indicates that the currently active connection is using ethernet. |
467 | Note: This value may also be returned when Windows is connected to a |
468 | Bluetooth personal area network. |
469 | \value Cellular |
470 | Indicates that the currently active connection is using a cellular |
471 | network. |
472 | \value WiFi |
473 | Indicates that the currently active connection is using Wi-Fi. |
474 | \value Bluetooth |
475 | Indicates that the currently active connection is connected using |
476 | Bluetooth. |
477 | |
478 | \sa QNetworkInformation::transportMedium |
479 | */ |
480 | |
481 | /*! |
482 | \internal ctor |
483 | */ |
484 | QNetworkInformation::QNetworkInformation(QNetworkInformationBackend *backend) |
485 | : QObject(*(new QNetworkInformationPrivate(backend))) |
486 | { |
487 | connect(sender: backend, signal: &QNetworkInformationBackend::reachabilityChanged, context: this, |
488 | slot: &QNetworkInformation::reachabilityChanged); |
489 | connect(sender: backend, signal: &QNetworkInformationBackend::behindCaptivePortalChanged, context: this, |
490 | slot: &QNetworkInformation::isBehindCaptivePortalChanged); |
491 | connect(sender: backend, signal: &QNetworkInformationBackend::transportMediumChanged, context: this, |
492 | slot: &QNetworkInformation::transportMediumChanged); |
493 | connect(sender: backend, signal: &QNetworkInformationBackend::isMeteredChanged, context: this, |
494 | slot: &QNetworkInformation::isMeteredChanged); |
495 | } |
496 | |
497 | /*! |
498 | \internal dtor |
499 | */ |
500 | QNetworkInformation::~QNetworkInformation() = default; |
501 | |
502 | /*! |
503 | \property QNetworkInformation::reachability |
504 | \brief The current state of the system's network connectivity. |
505 | |
506 | Indicates the level of connectivity that can be expected. Do note |
507 | that this is only based on what the plugin/operating system |
508 | reports. In certain scenarios this is known to be wrong. For |
509 | example, on Windows the 'Online' check, by default, is performed |
510 | by Windows connecting to a Microsoft-owned server. If this server |
511 | is for any reason blocked then it will assume it does not have |
512 | Online reachability. Because of this you should not use this as a |
513 | pre-check before attempting to make a connection. |
514 | */ |
515 | |
516 | QNetworkInformation::Reachability QNetworkInformation::reachability() const |
517 | { |
518 | return d_func()->backend->reachability(); |
519 | } |
520 | |
521 | /*! |
522 | \property QNetworkInformation::isBehindCaptivePortal |
523 | \brief Lets you know if the user's device is behind a captive portal. |
524 | \since 6.2 |
525 | |
526 | This property indicates if the user's device is currently known to be |
527 | behind a captive portal. This functionality relies on the operating system's |
528 | detection of captive portals and is not supported on systems that don't |
529 | report this. On systems where this is not supported this will always return |
530 | \c{false}. |
531 | */ |
532 | bool QNetworkInformation::isBehindCaptivePortal() const |
533 | { |
534 | return d_func()->backend->behindCaptivePortal(); |
535 | } |
536 | |
537 | /*! |
538 | \property QNetworkInformation::transportMedium |
539 | \brief The currently active transport medium for the application |
540 | \since 6.3 |
541 | |
542 | This property returns the currently active transport medium for the |
543 | application, on operating systems where such information is available. |
544 | |
545 | When the current transport medium changes a signal is emitted, this can, |
546 | for instance, occur when a user leaves the range of a WiFi network, unplugs |
547 | their ethernet cable or enables Airplane mode. |
548 | */ |
549 | QNetworkInformation::TransportMedium QNetworkInformation::transportMedium() const |
550 | { |
551 | return d_func()->backend->transportMedium(); |
552 | } |
553 | |
554 | /*! |
555 | \property QNetworkInformation::isMetered |
556 | \brief Check if the current connection is metered |
557 | \since 6.3 |
558 | |
559 | This property returns whether the current connection is (known to be) |
560 | metered or not. You can use this as a guiding factor to decide whether your |
561 | application should perform certain network requests or uploads. |
562 | For instance, you may not want to upload logs or diagnostics while this |
563 | property is \c true. |
564 | */ |
565 | bool QNetworkInformation::isMetered() const |
566 | { |
567 | return d_func()->backend->isMetered(); |
568 | } |
569 | |
570 | /*! |
571 | Returns the name of the currently loaded backend. |
572 | */ |
573 | QString QNetworkInformation::backendName() const |
574 | { |
575 | return d_func()->backend->name(); |
576 | } |
577 | |
578 | /*! |
579 | Returns \c true if the currently loaded backend supports |
580 | \a features. |
581 | */ |
582 | bool QNetworkInformation::supports(Features features) const |
583 | { |
584 | return (d_func()->backend->featuresSupported() & features) == features; |
585 | } |
586 | |
587 | /*! |
588 | \since 6.3 |
589 | |
590 | Returns all the supported features of the current backend. |
591 | */ |
592 | QNetworkInformation::Features QNetworkInformation::supportedFeatures() const |
593 | { |
594 | return d_func()->backend->featuresSupported(); |
595 | } |
596 | |
597 | /*! |
598 | \since 6.3 |
599 | |
600 | Attempts to load the platform-default backend. |
601 | |
602 | This platform-to-plugin mapping is as follows: |
603 | |
604 | \table |
605 | \header |
606 | \li Platform |
607 | \li Plugin-name |
608 | \row |
609 | \li Windows |
610 | \li networklistmanager |
611 | \row |
612 | \li Apple (macOS/iOS) |
613 | \li scnetworkreachability |
614 | \row |
615 | \li Android |
616 | \li android |
617 | \row |
618 | \li Linux |
619 | \li networkmanager |
620 | \endtable |
621 | |
622 | This function is provided for convenience where the default for a given |
623 | platform is good enough. If you are not using the default plugins you must |
624 | use one of the other load() overloads. |
625 | |
626 | Returns \c true if it managed to load the backend or if it was already |
627 | loaded. Returns \c false otherwise. |
628 | |
629 | \sa instance(), load() |
630 | */ |
631 | bool QNetworkInformation::loadDefaultBackend() |
632 | { |
633 | int index = -1; |
634 | #ifdef Q_OS_WIN |
635 | index = QNetworkInformationBackend::PluginNamesWindowsIndex; |
636 | #elif defined(Q_OS_DARWIN) |
637 | index = QNetworkInformationBackend::PluginNamesAppleIndex; |
638 | #elif defined(Q_OS_ANDROID) |
639 | index = QNetworkInformationBackend::PluginNamesAndroidIndex; |
640 | #elif defined(Q_OS_LINUX) |
641 | index = QNetworkInformationBackend::PluginNamesLinuxIndex; |
642 | #endif |
643 | if (index == -1) |
644 | return false; |
645 | return loadBackendByName(backend: QNetworkInformationBackend::PluginNames[index]); |
646 | } |
647 | |
648 | /*! |
649 | \since 6.4 |
650 | |
651 | Attempts to load a backend whose name matches \a backend |
652 | (case insensitively). |
653 | |
654 | Returns \c true if it managed to load the requested backend or |
655 | if it was already loaded. Returns \c false otherwise. |
656 | |
657 | \sa instance |
658 | */ |
659 | bool QNetworkInformation::loadBackendByName(QStringView backend) |
660 | { |
661 | auto loadedBackend = QNetworkInformationPrivate::create(name: backend); |
662 | return loadedBackend && loadedBackend->backendName().compare(s: backend, cs: Qt::CaseInsensitive) == 0; |
663 | } |
664 | |
665 | #if QT_DEPRECATED_SINCE(6,4) |
666 | /*! |
667 | \deprecated [6.4] Use loadBackendByName() instead. |
668 | |
669 | \sa loadBackendByName(), loadDefaultBackend(), loadBackendByFeatures() |
670 | */ |
671 | bool QNetworkInformation::load(QStringView backend) |
672 | { |
673 | return loadBackendByName(backend); |
674 | } |
675 | #endif // QT_DEPRECATED_SINCE(6,4) |
676 | |
677 | /*! |
678 | \since 6.4 |
679 | Load a backend which supports \a features. |
680 | |
681 | Returns \c true if it managed to load the requested backend or |
682 | if it was already loaded. Returns \c false otherwise. |
683 | |
684 | \sa instance |
685 | */ |
686 | bool QNetworkInformation::loadBackendByFeatures(Features features) |
687 | { |
688 | auto loadedBackend = QNetworkInformationPrivate::create(features); |
689 | return loadedBackend && loadedBackend->supports(features); |
690 | } |
691 | |
692 | #if QT_DEPRECATED_SINCE(6,4) |
693 | /*! |
694 | \deprecated [6.4] Use loadBackendByFeatures() instead. |
695 | |
696 | \sa loadBackendByName(), loadDefaultBackend(), loadBackendByFeatures() |
697 | */ |
698 | bool QNetworkInformation::load(Features features) |
699 | { |
700 | return loadBackendByFeatures(features); |
701 | } |
702 | #endif // QT_DEPRECATED_SINCE(6,4) |
703 | |
704 | /*! |
705 | Returns a list of the names of all currently available backends. |
706 | */ |
707 | QStringList QNetworkInformation::availableBackends() |
708 | { |
709 | return QNetworkInformationPrivate::backendNames(); |
710 | } |
711 | |
712 | /*! |
713 | Returns a pointer to the instance of the QNetworkInformation, |
714 | if any. |
715 | |
716 | \sa load() |
717 | */ |
718 | QNetworkInformation *QNetworkInformation::instance() |
719 | { |
720 | return QNetworkInformationPrivate::instance(); |
721 | } |
722 | |
723 | QT_END_NAMESPACE |
724 | |
725 | #include "moc_qnetworkinformation.cpp" |
726 | #include "moc_qnetworkinformation_p.cpp" |
727 | |