service/NetworkLifecycleCoordinator.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "NetworkLifecycleCoordinator.h" | ||
| 2 | |||
| 3 | #include <QCoreApplication> | ||
| 4 | #include <QThread> | ||
| 5 | #include <QCryptographicHash> | ||
| 6 | #include <QDBusConnection> | ||
| 7 | #include <QDBusMessage> | ||
| 8 | #include <QDBusPendingCallWatcher> | ||
| 9 | #include <QDBusPendingReply> | ||
| 10 | #include <QDBusObjectPath> | ||
| 11 | |||
| 12 | #include <QHostAddress> | ||
| 13 | #include <QHostInfo> | ||
| 14 | #include <QLoggingCategory> | ||
| 15 | #include <QNetworkInterface> | ||
| 16 | #include <QVariant> | ||
| 17 | |||
| 18 | #include <utility> | ||
| 19 | |||
| 20 |
4/7✓ Branch 2 → 3 taken 40 times.
✓ Branch 2 → 8 taken 24 times.
✓ Branch 4 → 5 taken 40 times.
✗ Branch 4 → 8 not taken.
✓ Branch 5 → 6 taken 40 times.
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 12 not taken.
|
64 | Q_LOGGING_CATEGORY(lcLifecycle, "mailjd.lifecycle") |
| 21 | |||
| 22 | 59 | NetworkLifecycleCoordinator::NetworkLifecycleCoordinator( | |
| 23 | 59 | QObject *parent, bool installNativeHooks) | |
| 24 |
3/3✓ Branch 3 → 4 taken 59 times.
✓ Branch 4 → 5 taken 59 times.
✓ Branch 6 → 7 taken 59 times.
|
59 | : QObject(parent) { |
| 25 |
1/1✓ Branch 12 → 13 taken 59 times.
|
59 | m_coalesceTimer.setSingleShot(true); |
| 26 |
1/1✓ Branch 13 → 14 taken 59 times.
|
59 | m_coalesceTimer.setInterval(500); |
| 27 | 59 | connect(&m_coalesceTimer, &QTimer::timeout, this, | |
| 28 |
1/1✓ Branch 14 → 15 taken 59 times.
|
59 | [this]() { m_coalescing = false; }); |
| 29 |
1/1✓ Branch 16 → 17 taken 59 times.
|
59 | m_identityTimer.setInterval(2000); |
| 30 | 59 | connect(&m_identityTimer, &QTimer::timeout, this, | |
| 31 |
1/1✓ Branch 17 → 18 taken 59 times.
|
59 | &NetworkLifecycleCoordinator::refreshNetworkIdentity); |
| 32 |
2/2✓ Branch 19 → 20 taken 39 times.
✓ Branch 19 → 26 taken 20 times.
|
59 | if (installNativeHooks) { |
| 33 |
1/1✓ Branch 20 → 21 taken 39 times.
|
39 | m_platformBus = QDBusConnection::systemBus(); |
| 34 |
1/1✓ Branch 23 → 24 taken 39 times.
|
39 | installNativeSleepHook(); |
| 35 |
1/1✓ Branch 24 → 25 taken 39 times.
|
39 | refreshNetworkIdentity(); |
| 36 |
1/1✓ Branch 25 → 26 taken 39 times.
|
39 | m_identityTimer.start(); |
| 37 | } | ||
| 38 | 59 | } | |
| 39 | |||
| 40 | 5034 | NetworkLifecycleCoordinator *NetworkLifecycleCoordinator::instance() { | |
| 41 | static NetworkLifecycleCoordinator *coordinator = | ||
| 42 |
6/12✓ Branch 2 → 3 taken 39 times.
✓ Branch 2 → 11 taken 4995 times.
✓ Branch 4 → 5 taken 39 times.
✗ Branch 4 → 11 not taken.
✓ Branch 5 → 6 taken 39 times.
✓ Branch 7 → 8 taken 39 times.
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 39 times.
✗ Branch 13 → 14 not taken.
✗ Branch 13 → 15 not taken.
✗ Branch 16 → 17 not taken.
✗ Branch 16 → 18 not taken.
|
5034 | new NetworkLifecycleCoordinator(QCoreApplication::instance()); |
| 43 | 5034 | return coordinator; | |
| 44 | } | ||
| 45 | |||
| 46 | 39 | void NetworkLifecycleCoordinator::installNativeSleepHook() { | |
| 47 | #ifdef Q_OS_LINUX | ||
| 48 |
3/3✓ Branch 2 → 3 taken 39 times.
✓ Branch 3 → 4 taken 39 times.
✓ Branch 16 → 17 taken 39 times.
|
78 | const bool connected = QDBusConnection::systemBus().connect( |
| 49 | 78 | QStringLiteral("org.freedesktop.login1"), | |
| 50 | 78 | QStringLiteral("/org/freedesktop/login1"), | |
| 51 | 78 | QStringLiteral("org.freedesktop.login1.Manager"), | |
| 52 | 78 | QStringLiteral("PrepareForSleep"), this, | |
| 53 | SLOT(prepareForSleep(bool))); | ||
| 54 |
5/5✓ Branch 26 → 27 taken 39 times.
✓ Branch 30 → 31 taken 39 times.
✓ Branch 31 → 32 taken 39 times.
✓ Branch 39 → 28 taken 39 times.
✓ Branch 39 → 40 taken 39 times.
|
78 | qCInfo(lcLifecycle) << "systemd-logind PrepareForSleep hook" |
| 55 |
2/3✗ Branch 32 → 33 not taken.
✓ Branch 32 → 34 taken 39 times.
✓ Branch 35 → 36 taken 39 times.
|
39 | << (connected ? "active" : "unavailable"); |
| 56 | #endif | ||
| 57 | 39 | } | |
| 58 | |||
| 59 | 9 | void NetworkLifecycleCoordinator::prepareForSleep(bool sleeping) { | |
| 60 |
2/2✓ Branch 2 → 3 taken 2 times.
✓ Branch 2 → 4 taken 7 times.
|
9 | if (sleeping == m_suspending) |
| 61 | 2 | return; | |
| 62 | 7 | m_suspending = sleeping; | |
| 63 | 7 | emit suspendingChanged(sleeping); | |
| 64 |
2/2✓ Branch 5 → 6 taken 3 times.
✓ Branch 5 → 7 taken 4 times.
|
7 | if (!sleeping) |
| 65 | 3 | requestRecovery(Trigger::NativeResume); | |
| 66 | } | ||
| 67 | |||
| 68 | quint64 | ||
| 69 | 31 | NetworkLifecycleCoordinator::requestRecovery( | |
| 70 | NetworkLifecycleCoordinator::Trigger trigger) { | ||
| 71 | // From another thread the request is queued; 0 = not yet assigned. | ||
| 72 |
3/3✓ Branch 3 → 4 taken 31 times.
✓ Branch 5 → 6 taken 1 time.
✓ Branch 5 → 7 taken 30 times.
|
32 | if (forwardToOwnThread([this, trigger] { requestRecovery(trigger); })) |
| 73 | 1 | return 0; | |
| 74 |
2/2✓ Branch 7 → 8 taken 5 times.
✓ Branch 7 → 9 taken 25 times.
|
30 | if (m_coalescing) |
| 75 | 5 | return m_generation; | |
| 76 | 25 | m_coalescing = true; | |
| 77 | 25 | ++m_generation; | |
| 78 |
7/7✓ Branch 9 → 10 taken 25 times.
✓ Branch 13 → 14 taken 25 times.
✓ Branch 14 → 15 taken 25 times.
✓ Branch 15 → 16 taken 25 times.
✓ Branch 16 → 17 taken 25 times.
✓ Branch 21 → 11 taken 25 times.
✓ Branch 21 → 22 taken 25 times.
|
50 | qCInfo(lcLifecycle) << "Recovery generation" << m_generation << "trigger" |
| 79 |
1/1✓ Branch 17 → 18 taken 25 times.
|
25 | << static_cast<int>(trigger); |
| 80 | 25 | emit recoveryRequested(m_generation, trigger); | |
| 81 | // F-60 (SPRINT-104): bound the coalescing window from the moment the | ||
| 82 | // request is emitted — including an unsuccessful recovery. A generation | ||
| 83 | // that never reaches stability (outage continues, no monitor | ||
| 84 | // acknowledges) must not stay open forever: suspend/resume during such | ||
| 85 | // a window suppressed the resume fan-out while the health monitor's | ||
| 86 | // timers were stopped, leaving IMAP with no scheduled retry at all. | ||
| 87 | // markStable restarts the same window for the late-failure case. | ||
| 88 | 25 | m_coalesceTimer.start(2000); | |
| 89 | 25 | return m_generation; | |
| 90 | } | ||
| 91 | |||
| 92 | 1295 | bool NetworkLifecycleCoordinator::forwardToOwnThread(std::function<void()> call) { | |
| 93 |
2/2✓ Branch 4 → 5 taken 1257 times.
✓ Branch 4 → 6 taken 38 times.
|
1295 | if (QThread::currentThread() == thread()) |
| 94 | 1257 | return false; | |
| 95 | 38 | QMetaObject::invokeMethod(this, std::move(call), Qt::QueuedConnection); | |
| 96 | 38 | return true; | |
| 97 | } | ||
| 98 | |||
| 99 | 86 | void NetworkLifecycleCoordinator::markStable(quint64 generation) { | |
| 100 |
3/3✓ Branch 3 → 4 taken 86 times.
✓ Branch 5 → 6 taken 25 times.
✓ Branch 5 → 7 taken 61 times.
|
111 | if (forwardToOwnThread([this, generation] { markStable(generation); })) |
| 101 | 25 | return; | |
| 102 |
2/2✓ Branch 7 → 8 taken 50 times.
✓ Branch 7 → 9 taken 11 times.
|
61 | if (generation == m_generation) { |
| 103 | 50 | m_stableGeneration = generation; | |
| 104 | // Keep the generation open briefly so main/body/search/sync restoration | ||
| 105 | // and late socket errors caused by the same handover cannot create a | ||
| 106 | // second recovery wave. | ||
| 107 | 50 | m_coalesceTimer.start(2000); | |
| 108 | } | ||
| 109 | } | ||
| 110 | |||
| 111 | 24 | bool NetworkLifecycleCoordinator::updateNetworkIdentity( | |
| 112 | const NetworkIdentity &identity) { | ||
| 113 |
2/2✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 5 taken 23 times.
|
24 | if (!identity.isValid()) |
| 114 | 1 | return false; | |
| 115 |
2/2✓ Branch 6 → 7 taken 11 times.
✓ Branch 6 → 9 taken 12 times.
|
23 | if (!m_networkIdentity.isValid()) { |
| 116 | 11 | m_networkIdentity = identity; | |
| 117 | 11 | return false; | |
| 118 | } | ||
| 119 | |||
| 120 | const bool changed = | ||
| 121 | 12 | identity.interfaceName != m_networkIdentity.interfaceName || | |
| 122 |
2/2✓ Branch 12 → 13 taken 8 times.
✓ Branch 12 → 17 taken 3 times.
|
11 | identity.activeAccessPointHash != |
| 123 |
2/2✓ Branch 14 → 15 taken 7 times.
✓ Branch 14 → 17 taken 1 time.
|
19 | m_networkIdentity.activeAccessPointHash || |
| 124 |
4/4✓ Branch 10 → 11 taken 11 times.
✓ Branch 10 → 17 taken 1 time.
✓ Branch 16 → 17 taken 4 times.
✓ Branch 16 → 18 taken 3 times.
|
31 | identity.sourceAddresses != m_networkIdentity.sourceAddresses || |
| 125 | 7 | identity.endpointAddresses != m_networkIdentity.endpointAddresses; | |
| 126 | 12 | m_networkIdentity = identity; | |
| 127 |
2/2✓ Branch 20 → 21 taken 9 times.
✓ Branch 20 → 22 taken 3 times.
|
12 | if (changed) |
| 128 | 9 | requestRecovery(Trigger::NetworkIdentity); | |
| 129 | 12 | return changed; | |
| 130 | } | ||
| 131 | |||
| 132 | 115 | void NetworkLifecycleCoordinator::observeEndpoint(const QString &host) { | |
| 133 |
4/4✓ Branch 3 → 4 taken 115 times.
✓ Branch 4 → 5 taken 115 times.
✓ Branch 7 → 8 taken 12 times.
✓ Branch 7 → 9 taken 103 times.
|
127 | if (forwardToOwnThread([this, host] { observeEndpoint(host); })) |
| 134 | 100 | return; | |
| 135 |
2/2✓ Branch 9 → 10 taken 103 times.
✓ Branch 10 → 11 taken 103 times.
|
103 | const QString normalized = host.trimmed().toLower(); |
| 136 |
6/6✓ Branch 13 → 14 taken 97 times.
✓ Branch 13 → 16 taken 6 times.
✓ Branch 15 → 16 taken 82 times.
✓ Branch 15 → 17 taken 15 times.
✓ Branch 18 → 19 taken 88 times.
✓ Branch 18 → 20 taken 15 times.
|
103 | if (normalized.isEmpty() || m_endpointHosts.contains(normalized)) |
| 137 | 88 | return; | |
| 138 |
1/1✓ Branch 20 → 21 taken 15 times.
|
15 | m_endpointHosts.insert(normalized); |
| 139 |
1/1✓ Branch 21 → 22 taken 15 times.
|
15 | refreshEndpointAddresses(); |
| 140 | 103 | } | |
| 141 | |||
| 142 | 68 | void NetworkLifecycleCoordinator::readNetworkManagerProperties( | |
| 143 | const QString &path, const QString &interface, | ||
| 144 | std::function<void(QVariantMap)> callback) { | ||
| 145 | auto message = QDBusMessage::createMethodCall( | ||
| 146 | 136 | QStringLiteral("org.freedesktop.NetworkManager"), path, | |
| 147 |
1/1✓ Branch 11 → 12 taken 68 times.
|
272 | QStringLiteral("org.freedesktop.DBus.Properties"), QStringLiteral("GetAll")); |
| 148 |
1/1✓ Branch 19 → 20 taken 68 times.
|
68 | message << interface; |
| 149 | // Construct messages directly: QDBusInterface can synchronously introspect. | ||
| 150 |
4/7✓ Branch 21 → 22 taken 68 times.
✓ Branch 22 → 23 taken 68 times.
✓ Branch 23 → 24 taken 68 times.
✗ Branch 25 → 26 not taken.
✓ Branch 25 → 27 taken 68 times.
✗ Branch 58 → 59 not taken.
✗ Branch 58 → 60 not taken.
|
68 | auto *watcher = new QDBusPendingCallWatcher(m_platformBus.asyncCall(message, 500), this); |
| 151 |
1/1✓ Branch 29 → 30 taken 68 times.
|
68 | connect(watcher, &QDBusPendingCallWatcher::finished, this, |
| 152 | 136 | [watcher, callback = std::move(callback)] { | |
| 153 |
1/1✓ Branch 2 → 3 taken 28 times.
|
28 | const QDBusPendingReply<QVariantMap> reply = *watcher; |
| 154 |
1/1✓ Branch 3 → 4 taken 28 times.
|
28 | watcher->deleteLater(); |
| 155 |
5/5✓ Branch 4 → 5 taken 28 times.
✓ Branch 5 → 6 taken 3 times.
✓ Branch 5 → 7 taken 25 times.
✓ Branch 7 → 8 taken 25 times.
✓ Branch 8 → 9 taken 28 times.
|
28 | callback(reply.isError() ? QVariantMap{} : reply.value()); |
| 156 | 28 | }); | |
| 157 | 68 | } | |
| 158 | |||
| 159 | 51 | void NetworkLifecycleCoordinator::readPlatformIdentity() { | |
| 160 | #ifdef Q_OS_LINUX | ||
| 161 |
1/1✓ Branch 9 → 10 taken 51 times.
|
51 | readNetworkManagerProperties(QStringLiteral("/org/freedesktop/NetworkManager"), |
| 162 | 153 | QStringLiteral("org.freedesktop.NetworkManager"), [this](const QVariantMap &root) { | |
| 163 |
2/2✓ Branch 6 → 7 taken 11 times.
✓ Branch 7 → 8 taken 11 times.
|
22 | const auto primary = qdbus_cast<QDBusObjectPath>(root.value(QStringLiteral("PrimaryConnection"))); |
| 164 |
9/10✓ Branch 14 → 15 taken 8 times.
✓ Branch 14 → 19 taken 3 times.
✓ Branch 18 → 19 taken 1 time.
✓ Branch 18 → 20 taken 7 times.
✓ Branch 21 → 22 taken 8 times.
✓ Branch 21 → 23 taken 3 times.
✓ Branch 23 → 24 taken 11 times.
✗ Branch 23 → 25 not taken.
✓ Branch 25 → 26 taken 4 times.
✓ Branch 25 → 31 taken 7 times.
|
11 | if (primary.path().isEmpty() || primary.path() == QLatin1String("/")) { |
| 165 | 4 | publishPlatformIdentity({}); | |
| 166 | 4 | return; | |
| 167 | } | ||
| 168 |
1/1✓ Branch 36 → 37 taken 7 times.
|
7 | readNetworkManagerProperties(primary.path(), |
| 169 | 21 | QStringLiteral("org.freedesktop.NetworkManager.Connection.Active"), [this](const QVariantMap &active) { | |
| 170 |
2/2✓ Branch 6 → 7 taken 7 times.
✓ Branch 7 → 8 taken 7 times.
|
14 | const auto devices = qdbus_cast<QList<QDBusObjectPath>>(active.value(QStringLiteral("Devices"))); |
| 171 |
2/2✓ Branch 13 → 14 taken 1 time.
✓ Branch 13 → 19 taken 6 times.
|
7 | if (devices.isEmpty()) { |
| 172 | 1 | publishPlatformIdentity({}); | |
| 173 | 1 | return; | |
| 174 | } | ||
| 175 | 6 | const auto devicePath = devices.first().path(); | |
| 176 |
2/2✓ Branch 22 → 23 taken 6 times.
✓ Branch 26 → 27 taken 6 times.
|
12 | readNetworkManagerProperties(devicePath, |
| 177 | 18 | QStringLiteral("org.freedesktop.NetworkManager.Device"), [this, devicePath](const QVariantMap &device) { | |
| 178 | 6 | NetworkIdentity identity; | |
| 179 |
2/2✓ Branch 7 → 8 taken 6 times.
✓ Branch 8 → 9 taken 6 times.
|
12 | identity.interfaceName = device.value(QStringLiteral("Interface")).toString(); |
| 180 | 6 | identity.backend = QStringLiteral("NetworkManager"); | |
| 181 |
16/24✓ Branch 22 → 23 taken 5 times.
✓ Branch 22 → 30 taken 1 time.
✓ Branch 27 → 28 taken 5 times.
✓ Branch 28 → 29 taken 5 times.
✓ Branch 29 → 30 taken 1 time.
✓ Branch 29 → 31 taken 4 times.
✓ Branch 32 → 33 taken 5 times.
✓ Branch 32 → 34 taken 1 time.
✓ Branch 34 → 35 taken 5 times.
✓ Branch 34 → 36 taken 1 time.
✓ Branch 36 → 37 taken 5 times.
✓ Branch 36 → 38 taken 1 time.
✓ Branch 38 → 39 taken 5 times.
✓ Branch 38 → 40 taken 1 time.
✓ Branch 40 → 41 taken 2 times.
✓ Branch 40 → 46 taken 4 times.
✗ Branch 77 → 78 not taken.
✗ Branch 77 → 79 not taken.
✗ Branch 81 → 82 not taken.
✗ Branch 81 → 83 not taken.
✗ Branch 85 → 86 not taken.
✗ Branch 85 → 87 not taken.
✗ Branch 89 → 90 not taken.
✗ Branch 89 → 91 not taken.
|
11 | if (identity.interfaceName.isEmpty() || device.value(QStringLiteral("DeviceType")).toUInt() != 2) { |
| 182 |
1/1✓ Branch 43 → 44 taken 2 times.
|
2 | publishPlatformIdentity(std::move(identity)); |
| 183 | 2 | return; | |
| 184 | } | ||
| 185 |
2/2✓ Branch 48 → 49 taken 4 times.
✓ Branch 52 → 53 taken 4 times.
|
8 | readNetworkManagerProperties(devicePath, |
| 186 | 8 | QStringLiteral("org.freedesktop.NetworkManager.Device.Wireless"), | |
| 187 | 8 | [this, identity = std::move(identity)](const QVariantMap &wireless) mutable { | |
| 188 |
2/2✓ Branch 6 → 7 taken 4 times.
✓ Branch 7 → 8 taken 4 times.
|
8 | const auto accessPoint = qdbus_cast<QDBusObjectPath>(wireless.value(QStringLiteral("ActiveAccessPoint"))); |
| 189 |
7/10✓ Branch 14 → 15 taken 4 times.
✗ Branch 14 → 20 not taken.
✓ Branch 18 → 19 taken 3 times.
✓ Branch 18 → 20 taken 1 time.
✓ Branch 21 → 22 taken 4 times.
✗ Branch 21 → 23 not taken.
✓ Branch 23 → 24 taken 4 times.
✗ Branch 23 → 25 not taken.
✓ Branch 25 → 26 taken 3 times.
✓ Branch 25 → 35 taken 1 time.
|
4 | if (!accessPoint.path().isEmpty() && accessPoint.path() != QLatin1String("/")) |
| 190 |
2/2✓ Branch 27 → 28 taken 3 times.
✓ Branch 29 → 30 taken 3 times.
|
3 | identity.activeAccessPointHash = QCryptographicHash::hash(accessPoint.path().toUtf8(), QCryptographicHash::Sha256); |
| 191 |
1/1✓ Branch 37 → 38 taken 4 times.
|
4 | publishPlatformIdentity(std::move(identity)); |
| 192 | 4 | }); | |
| 193 |
2/2✓ Branch 59 → 60 taken 4 times.
✓ Branch 59 → 62 taken 2 times.
|
6 | }); |
| 194 |
3/11✓ Branch 16 → 17 taken 1 time.
✓ Branch 34 → 35 taken 6 times.
✓ Branch 34 → 37 taken 1 time.
✗ Branch 53 → 54 not taken.
✗ Branch 53 → 55 not taken.
✗ Branch 56 → 57 not taken.
✗ Branch 56 → 58 not taken.
✗ Branch 59 → 60 not taken.
✗ Branch 59 → 61 not taken.
✗ Branch 62 → 63 not taken.
✗ Branch 62 → 64 not taken.
|
8 | }); |
| 195 |
3/11✓ Branch 28 → 29 taken 4 times.
✓ Branch 43 → 44 taken 7 times.
✓ Branch 43 → 46 taken 4 times.
✗ Branch 62 → 63 not taken.
✗ Branch 62 → 64 not taken.
✗ Branch 65 → 66 not taken.
✗ Branch 65 → 67 not taken.
✗ Branch 68 → 69 not taken.
✗ Branch 68 → 70 not taken.
✗ Branch 71 → 72 not taken.
✗ Branch 71 → 73 not taken.
|
15 | }); |
| 196 | #else | ||
| 197 | publishPlatformIdentity({}); | ||
| 198 | #endif | ||
| 199 | 51 | } | |
| 200 | |||
| 201 | 20 | NetworkIdentity NetworkLifecycleCoordinator::completePlatformIdentity(NetworkIdentity identity) const { | |
| 202 |
2/2✓ Branch 3 → 4 taken 15 times.
✓ Branch 3 → 32 taken 5 times.
|
20 | if (identity.interfaceName.isEmpty()) { |
| 203 | // Portable bounded fallback: choose one active non-loopback interface. | ||
| 204 | // It cannot identify every default-route change, but avoids reconnecting | ||
| 205 | // on address churn from unrelated inactive interfaces. | ||
| 206 | 15 | for (const QNetworkInterface &candidate : | |
| 207 |
4/5✓ Branch 4 → 5 taken 15 times.
✓ Branch 5 → 6 taken 15 times.
✓ Branch 6 → 7 taken 15 times.
✓ Branch 29 → 8 taken 30 times.
✗ Branch 29 → 30 not taken.
|
45 | QNetworkInterface::allInterfaces()) { |
| 208 |
1/1✓ Branch 9 → 10 taken 30 times.
|
30 | const auto flags = candidate.flags(); |
| 209 |
5/6✓ Branch 11 → 12 taken 30 times.
✗ Branch 11 → 14 not taken.
✓ Branch 13 → 14 taken 15 times.
✓ Branch 13 → 15 taken 15 times.
✓ Branch 16 → 17 taken 15 times.
✓ Branch 16 → 18 taken 15 times.
|
60 | if (!flags.testFlag(QNetworkInterface::IsUp) || |
| 210 | 30 | flags.testFlag(QNetworkInterface::IsLoopBack)) | |
| 211 | 15 | continue; | |
| 212 | 15 | identity.backend = QStringLiteral("QtNetworkInterface"); | |
| 213 |
1/1✓ Branch 24 → 25 taken 15 times.
|
15 | identity.interfaceName = candidate.name(); |
| 214 | 15 | break; | |
| 215 | 15 | } | |
| 216 | } | ||
| 217 | |||
| 218 |
1/2✓ Branch 33 → 34 taken 20 times.
✗ Branch 33 → 61 not taken.
|
20 | if (!identity.interfaceName.isEmpty()) { |
| 219 | const QNetworkInterface active = | ||
| 220 |
1/1✓ Branch 34 → 35 taken 20 times.
|
20 | QNetworkInterface::interfaceFromName(identity.interfaceName); |
| 221 |
5/5✓ Branch 35 → 36 taken 20 times.
✓ Branch 36 → 37 taken 20 times.
✓ Branch 37 → 38 taken 20 times.
✓ Branch 57 → 39 taken 15 times.
✓ Branch 57 → 58 taken 20 times.
|
35 | for (const QNetworkAddressEntry &entry : active.addressEntries()) { |
| 222 |
1/1✓ Branch 40 → 41 taken 15 times.
|
15 | const QHostAddress address = entry.ip(); |
| 223 |
5/8✓ Branch 41 → 42 taken 15 times.
✓ Branch 42 → 43 taken 15 times.
✗ Branch 42 → 48 not taken.
✓ Branch 43 → 44 taken 15 times.
✓ Branch 44 → 45 taken 15 times.
✗ Branch 44 → 48 not taken.
✓ Branch 49 → 50 taken 15 times.
✗ Branch 49 → 54 not taken.
|
30 | if (!address.isNull() && !address.isLoopback() && |
| 224 |
2/3✓ Branch 45 → 46 taken 15 times.
✓ Branch 46 → 47 taken 15 times.
✗ Branch 46 → 48 not taken.
|
15 | !address.isLinkLocal()) { |
| 225 |
2/2✓ Branch 50 → 51 taken 15 times.
✓ Branch 51 → 52 taken 15 times.
|
15 | identity.sourceAddresses.insert(address.toString()); |
| 226 | } | ||
| 227 | 35 | } | |
| 228 | 20 | } | |
| 229 | 20 | for (auto it = m_resolvedEndpoints.cbegin(); | |
| 230 |
2/2✓ Branch 67 → 62 taken 9 times.
✓ Branch 67 → 68 taken 20 times.
|
29 | it != m_resolvedEndpoints.cend(); ++it) { |
| 231 |
1/1✓ Branch 63 → 64 taken 9 times.
|
9 | identity.endpointAddresses.unite(it.value()); |
| 232 | } | ||
| 233 | 20 | return identity; | |
| 234 | } | ||
| 235 | |||
| 236 | 1079 | void NetworkLifecycleCoordinator::refreshEndpointAddresses() { | |
| 237 |
2/2✓ Branch 16 → 5 taken 44 times.
✓ Branch 16 → 17 taken 1079 times.
|
1123 | for (const QString &host : std::as_const(m_endpointHosts)) { |
| 238 |
2/2✓ Branch 7 → 8 taken 1 time.
✓ Branch 7 → 9 taken 43 times.
|
44 | if (m_endpointLookups.contains(host)) |
| 239 | 1 | continue; | |
| 240 |
1/1✓ Branch 9 → 10 taken 43 times.
|
43 | m_endpointLookups.insert(host); |
| 241 |
1/1✓ Branch 11 → 12 taken 43 times.
|
43 | QHostInfo::lookupHost(host, this, [this, host](const QHostInfo &info) { |
| 242 |
1/1✓ Branch 2 → 3 taken 40 times.
|
40 | m_endpointLookups.remove(host); |
| 243 |
3/3✓ Branch 3 → 4 taken 40 times.
✓ Branch 4 → 5 taken 25 times.
✓ Branch 4 → 6 taken 15 times.
|
40 | if (info.error() != QHostInfo::NoError) |
| 244 | 25 | return; | |
| 245 | 15 | QSet<QString> addresses; | |
| 246 |
5/5✓ Branch 7 → 8 taken 15 times.
✓ Branch 8 → 9 taken 15 times.
✓ Branch 9 → 10 taken 15 times.
✓ Branch 17 → 11 taken 15 times.
✓ Branch 17 → 18 taken 15 times.
|
30 | for (const QHostAddress &address : info.addresses()) |
| 247 |
2/2✓ Branch 12 → 13 taken 15 times.
✓ Branch 13 → 14 taken 15 times.
|
30 | addresses.insert(address.toString()); |
| 248 |
1/1✓ Branch 19 → 20 taken 15 times.
|
15 | applyEndpointResolution(host, addresses); |
| 249 | 15 | }); | |
| 250 | } | ||
| 251 | 1079 | } | |
| 252 | |||
| 253 | 21 | void NetworkLifecycleCoordinator::applyEndpointResolution( | |
| 254 | const QString &host, const QSet<QString> &addresses) { | ||
| 255 | 21 | const bool hadResolution = m_resolvedEndpoints.contains(host); | |
| 256 |
8/8✓ Branch 3 → 4 taken 10 times.
✓ Branch 3 → 8 taken 11 times.
✓ Branch 6 → 7 taken 8 times.
✓ Branch 6 → 8 taken 2 times.
✓ Branch 9 → 10 taken 10 times.
✓ Branch 9 → 11 taken 11 times.
✓ Branch 11 → 12 taken 8 times.
✓ Branch 11 → 13 taken 13 times.
|
21 | if (hadResolution && addresses == m_resolvedEndpoints.value(host)) |
| 257 | 19 | return; | |
| 258 | |||
| 259 |
1/1✓ Branch 13 → 14 taken 13 times.
|
13 | m_resolvedEndpoints.insert(host, addresses); |
| 260 | // DNS changes only the endpoint part of the current platform snapshot. | ||
| 261 | // It must not synchronously query NetworkManager for every resolved host. | ||
| 262 | 13 | NetworkIdentity identity = m_networkIdentity; | |
| 263 | 13 | identity.endpointAddresses.clear(); | |
| 264 |
3/3✓ Branch 17 → 18 taken 9 times.
✓ Branch 17 → 25 taken 4 times.
✓ Branch 20 → 21 taken 9 times.
|
13 | if (!identity.isValid()) identity = completePlatformIdentity(std::move(identity)); |
| 265 |
2/2✓ Branch 32 → 28 taken 4 times.
✓ Branch 32 → 33 taken 4 times.
|
8 | else for (const auto &resolved : std::as_const(m_resolvedEndpoints)) |
| 266 |
1/1✓ Branch 29 → 30 taken 4 times.
|
4 | identity.endpointAddresses.unite(resolved); |
| 267 |
2/2✓ Branch 34 → 35 taken 11 times.
✓ Branch 34 → 40 taken 2 times.
|
13 | if (!hadResolution) { |
| 268 | // The first DNS answer establishes the endpoint baseline. Treating the | ||
| 269 | // empty-to-resolved transition as a handover tears down the connection | ||
| 270 | // that initiated this lookup during every application start. | ||
| 271 |
2/2✓ Branch 36 → 37 taken 9 times.
✓ Branch 36 → 38 taken 2 times.
|
11 | if (!m_networkIdentity.isValid()) |
| 272 | 9 | m_networkIdentity = identity; | |
| 273 | else | ||
| 274 | 2 | m_networkIdentity.endpointAddresses = identity.endpointAddresses; | |
| 275 | 11 | return; | |
| 276 | } | ||
| 277 |
1/1✓ Branch 40 → 41 taken 2 times.
|
2 | updateNetworkIdentity(identity); |
| 278 | 13 | } | |
| 279 | |||
| 280 | 1063 | void NetworkLifecycleCoordinator::refreshNetworkIdentity() { | |
| 281 |
2/3✓ Branch 3 → 4 taken 1063 times.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 1063 times.
|
1063 | if (forwardToOwnThread([this] { refreshNetworkIdentity(); })) |
| 282 | ✗ | return; | |
| 283 | 1063 | refreshEndpointAddresses(); | |
| 284 |
2/2✓ Branch 8 → 9 taken 1012 times.
✓ Branch 8 → 10 taken 51 times.
|
1063 | if (m_identityReading) { |
| 285 | 1012 | m_identityRefreshPending = true; | |
| 286 | 1012 | return; | |
| 287 | } | ||
| 288 | 51 | m_identityReading = true; | |
| 289 | 51 | readPlatformIdentity(); | |
| 290 | } | ||
| 291 | |||
| 292 | 11 | void NetworkLifecycleCoordinator::publishPlatformIdentity(NetworkIdentity identity) { | |
| 293 | 11 | m_identityReading = false; | |
| 294 |
2/2✓ Branch 4 → 5 taken 11 times.
✓ Branch 5 → 6 taken 11 times.
|
11 | updateNetworkIdentity(completePlatformIdentity(std::move(identity))); |
| 295 |
2/2✓ Branch 9 → 10 taken 1 time.
✓ Branch 9 → 12 taken 10 times.
|
11 | if (std::exchange(m_identityRefreshPending, false)) |
| 296 |
1/1✓ Branch 10 → 11 taken 1 time.
|
1 | QTimer::singleShot(0, this, &NetworkLifecycleCoordinator::refreshNetworkIdentity); |
| 297 | 11 | } | |
| 298 |