Skip to content

fix: Fix incorrect network selection after disabling and re-enabling network interface#518

Merged
deepin-bot[bot] merged 1 commit intolinuxdeepin:masterfrom
ut003640:master
Mar 9, 2026
Merged

fix: Fix incorrect network selection after disabling and re-enabling network interface#518
deepin-bot[bot] merged 1 commit intolinuxdeepin:masterfrom
ut003640:master

Conversation

@ut003640
Copy link
Contributor

@ut003640 ut003640 commented Mar 9, 2026

After a successful connection, the latest connection time failed to save correctly, leading to errors when manually retrieving connections. The solution involves saving the UUID of the currently successful connection to a configuration file. When the network interface is re-enabled, the connection configuration is read directly from this configuration file and actively reconnected.

Log: Fix reconnection error after enabling network interface
BUG: PMS-350785

fix: 修复关闭再开启后选择连接网络错误

在连接成功后,最新的连接的时间保存失败,手动获取的连接错误,修改为保存当前连接成功的连接的uuid到配置文件,开启网卡后直接从配置文件读取连接配置并主动连接

Log: 修复开启网卡后回连错误的问题

Summary by Sourcery

Persist and reuse the last successful network connection per device to ensure correct reconnection after disabling and re-enabling network interfaces.

Bug Fixes:

  • Fix incorrect network selection when re-enabling wired or wireless interfaces by reconnecting using the last successful connection.
  • Prevent failed disconnections by ensuring devices in transitional states are explicitly disconnected when disabled.

Enhancements:

  • Store per-device connection UUIDs in the network enabled configuration and load them on startup to restore the last successful connection.
  • Simplify device enable handling in the manager layer by removing now-unneeded DBus reply-based reconnection logic.

@sourcery-ai
Copy link

sourcery-ai bot commented Mar 9, 2026

Reviewer's Guide

Persists the last successfully activated connection per device in a config file and uses it to restore the correct network after re‑enabling a network interface, while removing now‑redundant per‑device DBus enable callbacks and auto‑reconnect logic.

Sequence diagram for device re-enable reconnection using stored connection UUID

sequenceDiagram
    actor User
    participant NetworkManagerDevice as NetworkManagerDevice
    participant NetworkThread as NetworkThread
    participant NetworkEnabledConfig as NetworkEnabledConfig
    participant NetworkManager as NetworkManager

    User->>NetworkManagerDevice: disable interface
    NetworkManagerDevice-->>NetworkThread: onDevicestateChanged(newState, oldState)
    NetworkThread->>NetworkEnabledConfig: deviceEnabled(dev->uni())
    NetworkEnabledConfig-->>NetworkThread: enabled = false
    NetworkThread->>NetworkManagerDevice: disconnectInterface() (if state Preparing..Activated)

    User->>NetworkManagerDevice: enable interface
    NetworkManagerDevice-->>NetworkThread: onDevicestateChanged(newState, oldState)
    NetworkThread->>NetworkEnabledConfig: deviceEnabled(dev->uni())
    NetworkEnabledConfig-->>NetworkThread: enabled = true
    alt state == Activated
        NetworkThread->>NetworkManagerDevice: activeConnection()
        NetworkManagerDevice-->>NetworkThread: ActiveConnection
        NetworkThread->>NetworkEnabledConfig: setConnectionInfo(interfaceName, connection->uuid())
        NetworkThread->>NetworkEnabledConfig: saveConfig()
    end

    User->>NetworkThread: enableDevice(device)
    NetworkThread->>NetworkEnabledConfig: connectionUuid(device->interfaceName())
    NetworkEnabledConfig-->>NetworkThread: last_uuid or empty
    alt last_uuid not empty and autoconnect
        NetworkThread->>NetworkManager: findConnectionByUuid(last_uuid)
        NetworkManager-->>NetworkThread: Connection
        NetworkThread->>NetworkManager: activateConnection(connPath0, device->uni(), "")
    else no valid stored connection
        NetworkThread->>NetworkManagerDevice: availableConnections()
        NetworkManagerDevice-->>NetworkThread: ConnectionList
        NetworkThread->>NetworkThread: pick autoconnect connection with max timestamp
        NetworkThread->>NetworkManager: activateConnection(connPath0, device->uni(), "")
    end
Loading

ER diagram for NetworkEnabledConfig JSON structure with Connections map

erDiagram
    NetworkEnabledConfig {
        string filePath
    }

    DeviceEntry {
        string deviceUni
        boolean enabled
    }

    ConnectionEntry {
        string interfaceName
        string connectionUuid
    }

    NetworkEnabledConfig ||--o{ DeviceEntry : has
    NetworkEnabledConfig ||--o{ ConnectionEntry : has
Loading

Class diagram for NetworkEnabledConfig, NetworkThread, and device manager classes

classDiagram

    class NetworkEnabledConfig {
        - QMap~QString, QVariant~ m_map
        + NetworkEnabledConfig()
        + void loadConfig()
        + QString saveConfig()
        + void setConnectionInfo(QString dev, QString uuid)
        + QString connectionUuid(QString dev) const
        + bool deviceEnabled(QString devUni) const
        + void setDeviceEnabled(QString devUni, bool enabled)
        + bool vpnEnabled() const
        + void setVpnEnabled(bool enabled)
    }

    class NetworkThread {
        - NetworkEnabledConfig* m_networkConfig
        + void onDevicestateChanged(Device_State newState, Device_State oldState, NetworkManager_Device_Ptr dev)
        + QString enableDevice(NetworkManager_Device_Ptr device)
        + QString setPropVpnEnabled(bool enabled)
    }

    class DeviceManagerRealize {
        + DeviceManagerRealize(QObject* parent)
        + ~DeviceManagerRealize()
        + void setEnabled(bool enabled)
        + void disconnectNetwork() = 0
        + void addConnection(NetworkManager_Connection_Ptr connection) = 0
        + void removeConnection(QString connection) = 0
        + void onActiveConnectionChanged() = 0
        + void deviceEnabledChanged(bool enabled)
    }

    class WiredDeviceManagerRealize {
        + WiredDeviceManagerRealize(QObject* parent)
        + ~WiredDeviceManagerRealize()
        + void disconnectNetwork()
        + void addConnection(NetworkManager_Connection_Ptr connection)
        + void removeConnection(QString connectionUni)
        + void onActiveConnectionChanged()
        + QString usingHwAdr() const
        + bool carrier() const
    }

    class WirelessDeviceManagerRealize {
        + WirelessDeviceManagerRealize(QObject* parent)
        + ~WirelessDeviceManagerRealize()
        + void disconnectNetwork()
        + void addConnection(NetworkManager_Connection_Ptr connection)
        + void removeConnection(QString connectionUni)
        + void onActiveConnectionChanged()
        + DeviceStatus deviceStatus() const
        + void deviceEnabledChanged(bool enabled)
        + QString usingHwAdr() const
    }

    NetworkThread --> NetworkEnabledConfig : uses
    WiredDeviceManagerRealize --|> DeviceManagerRealize
    WirelessDeviceManagerRealize --|> DeviceManagerRealize
Loading

File-Level Changes

Change Details Files
Record the UUID of the currently successful active connection on state changes and persist it to the network enabled configuration.
  • On device state change, when a device is enabled and reaches Activated, obtain its active connection and store the connection UUID keyed by interface name in NetworkEnabledConfig.
  • Invoke NetworkEnabledConfig::saveConfig after updating the stored connection UUID to ensure it is written to disk.
network-service-plugin/src/system/networkthread.cpp
network-service-plugin/src/system/networkenabledconfig.cpp
network-service-plugin/src/system/networkenabledconfig.h
Use the stored connection UUID to choose which connection to activate when re‑enabling a device, falling back to the latest autoconnect connection when no stored UUID is available.
  • In enableDevice, first look up the last stored connection UUID for the device and, if it exists and is autoconnect-enabled, select its path as the activation target.
  • If no valid stored UUID is found, iterate availableConnections, filter by autoconnect, and pick the one with the most recent timestamp.
  • If a target connection path is found, explicitly call NetworkManager::activateConnection with that path and the device UNI.
network-service-plugin/src/system/networkthread.cpp
network-service-plugin/src/system/networkenabledconfig.cpp
network-service-plugin/src/system/networkenabledconfig.h
Extend NetworkEnabledConfig to persist per-device last-connection information alongside device enablement state.
  • During loadConfig, read a new "Connections" JSON object mapping interface names to connection UUIDs into the internal map.
  • During saveConfig, serialize the in-memory "Connections" map back into the JSON config file.
  • Expose setConnectionInfo and connectionUuid methods to update and query stored connection UUIDs by device name.
network-service-plugin/src/system/networkenabledconfig.cpp
network-service-plugin/src/system/networkenabledconfig.h
Simplify device enable handling by removing now-unnecessary deviceEnabledAction hooks in wired and wireless device realizations and the associated DBus reply handling.
  • Stop capturing the QDBusReply from the EnableDevice call in DeviceManagerRealize::setEnabled and remove the call to deviceEnabledAction.
  • Delete deviceEnabledAction overrides and their auto-reconnect logic from WiredDeviceManagerRealize and WirelessDeviceManagerRealize.
  • Remove the now-unused virtual deviceEnabledAction declaration from DeviceManagerRealize and its subclasses.
src/impl/networkmanager/devicemanagerrealize.cpp
src/impl/networkmanager/devicemanagerrealize.h

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • In NetworkThread::onDevicestateChanged, consider guarding activeConnection->connection() before dereferencing to get the UUID, as ActiveConnection::Ptr may be valid while its associated connection() is null in some transitional states.
  • In NetworkThread::enableDevice, the new qDebug() call for the successful connection is inconsistent with the surrounding logging (qCDebug(DSM)); using the same debug macro and category would keep log output uniform and easier to filter.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `NetworkThread::onDevicestateChanged`, consider guarding `activeConnection->connection()` before dereferencing to get the UUID, as `ActiveConnection::Ptr` may be valid while its associated `connection()` is null in some transitional states.
- In `NetworkThread::enableDevice`, the new `qDebug()` call for the successful connection is inconsistent with the surrounding logging (`qCDebug(DSM)`); using the same debug macro and category would keep log output uniform and easier to filter.

## Individual Comments

### Comment 1
<location path="network-service-plugin/src/system/networkthread.cpp" line_range="313-315" />
<code_context>
+            }
         }
     }
+    if (!connPath0.isEmpty()) {
+        NetworkManager::activateConnection(connPath0, device->uni(), QString());
+        qDebug() << "connected:" << connPath0;
+    }

</code_context>
<issue_to_address>
**issue (bug_risk):** Connection activation now occurs before checking/enabling global networking state.

This changes the previous behavior where activation only occurred after confirming/enabling `NetworkManager::isNetworkingEnabled()`. Now `NetworkManager::activateConnection` can be called while networking is disabled, which may cause failures or different semantics. Please either restore activation to after the networking-enabled check or explicitly handle/document the disabled-networking case when calling `activateConnection`.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +313 to +315
if (!connPath0.isEmpty()) {
NetworkManager::activateConnection(connPath0, device->uni(), QString());
qDebug() << "connected:" << connPath0;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Connection activation now occurs before checking/enabling global networking state.

This changes the previous behavior where activation only occurred after confirming/enabling NetworkManager::isNetworkingEnabled(). Now NetworkManager::activateConnection can be called while networking is disabled, which may cause failures or different semantics. Please either restore activation to after the networking-enabled check or explicitly handle/document the disabled-networking case when calling activateConnection.

…network interface

After a successful connection, the latest connection time failed to save correctly, leading to errors when manually retrieving connections. The solution involves saving the UUID of the currently successful connection to a configuration file. When the network interface is re-enabled, the connection configuration is read directly from this configuration file and actively reconnected.

Log: Fix reconnection error after enabling network interface
BUG: PMS-350785

fix: 修复关闭再开启后选择连接网络错误

在连接成功后,最新的连接的时间保存失败,手动获取的连接错误,修改为保存当前连接成功的连接的uuid到配置文件,开启网卡后直接从配置文件读取连接配置并主动连接

Log: 修复开启网卡后回连错误的问题
@deepin-ci-robot
Copy link

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: caixr23, ut003640

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@deepin-ci-robot
Copy link

deepin pr auto review

代码审查报告

总体概述

这次代码变更主要是为了改进网络设备的连接管理逻辑,特别是保存和恢复网络设备的连接信息。主要变更包括添加了连接信息的持久化存储、修改了设备启用时的连接逻辑,以及删除了一些不再需要的代码。

语法和逻辑分析

1. NetworkEnabledConfig 类变更

优点:

  • 新增了 setConnectionInfo()connectionUuid() 方法,用于管理设备连接信息
  • loadConfig()saveConfig() 中添加了对连接信息的处理

问题:

  1. 性能问题:在 loadConfig()saveConfig() 中,使用了 QVariantMap 来存储连接信息,这可能导致不必要的类型转换开销。
  2. 错误处理不足:在 setConnectionInfo()connectionUuid() 方法中,没有对输入参数进行有效性检查。
  3. 代码重复:在 loadConfig()saveConfig() 中,处理连接信息的代码模式相似,可以抽取为私有方法。

改进建议:

// 在 NetworkEnabledConfig 类中添加私有方法
private:
    QVariantMap loadConnections(const QJsonObject &jsonObj) {
        QJsonObject connObj = jsonObj.value("Connections").toObject();
        QVariantMap conn_map;
        for (const QString &conn_key : connObj.keys()) {
            QString curr_obj_value = connObj.value(conn_key).toString();
            conn_map.insert(conn_key, curr_obj_value);
        }
        return conn_map;
    }
    
    QJsonObject saveConnections(const QVariantMap &conn_map) {
        QJsonObject connectionObj;
        for (auto it = conn_map.constBegin(); it != conn_map.constEnd(); it++) {
            connectionObj.insert(it.key(), it.value().toString());
        }
        return connectionObj;
    }

// 修改 setConnectionInfo 方法,添加参数检查
void NetworkEnabledConfig::setConnectionInfo(const QString &dev, const QString &uuid)
{
    if (dev.isEmpty() || uuid.isEmpty()) {
        qCWarning(DSM()) << "Invalid parameters for setConnectionInfo";
        return;
    }
    
    QVariant conn = m_map["Connections"];
    QVariantMap conn_map = conn.value<QVariantMap>();
    conn_map[dev] = uuid;
    m_map["Connections"] = conn_map;
}

// 修改 connectionUuid 方法,添加参数检查
QString NetworkEnabledConfig::connectionUuid(const QString &dev) const
{
    if (dev.isEmpty()) {
        qCWarning(DSM()) << "Invalid parameter for connectionUuid";
        return QString();
    }
    
    QVariant conn = m_map["Connections"];
    QVariantMap conn_map = conn.value<QVariantMap>();
    if (conn_map.contains(dev))
        return conn_map.value(dev).toString();

    return QString();
}

2. NetworkThread 类变更

优点:

  • 在设备状态变化时保存连接信息,确保下次启用时可以恢复连接
  • 修改了 enableDevice() 方法,优先使用保存的连接信息

问题:

  1. 逻辑问题:在 onDevicestateChanged() 中,只有当设备已启用且处于激活状态时才保存连接信息,但可能在连接尚未完全建立时调用。
  2. 性能问题:每次设备状态变化都调用 saveConfig(),可能导致频繁的文件I/O操作。
  3. 错误处理不足:在 enableDevice() 中,没有检查 current_connection 是否有效。

改进建议:

// 修改 onDevicestateChanged 方法
void NetworkThread::onDevicestateChanged(NetworkManager::Device::State newState, NetworkManager::Device::State oldState, NetworkManager::Device::StateChangeReason reason)
{
    // ... 现有代码 ...
    
    Device::State state = dev->state();
    if (enabled) {
        if (state == Device::Activated) {
            qCDebug(DSM) << "device connection success";
            NetworkManager::ActiveConnection::Ptr activeConnection = dev->activeConnection();
            if (activeConnection && activeConnection->connection()) {
                m_networkConfig->setConnectionInfo(dev->interfaceName(), activeConnection->connection()->uuid());
                // 考虑使用定时器延迟保存,避免频繁I/O
                m_saveConfigTimer.start(1000); // 1秒后保存
            }
        }
    } else {
        // ... 现有代码 ...
    }
}

// 修改 enableDevice 方法
QString NetworkThread::enableDevice(NetworkManager::Device::Ptr device)
{
    QString connPath0;
    const QString &device_uuid = m_networkConfig->connectionUuid(device->interfaceName());
    if (!device_uuid.isEmpty()) {
        NetworkManager::Connection::Ptr current_connection = NetworkManager::findConnectionByUuid(device_uuid);
        if (current_connection && current_connection->settings()->autoconnect()) {
            connPath0 = current_connection->path();
        }
    }
    if (connPath0.isEmpty()) {
        // ... 现有代码 ...
    }
    
    // 添加错误检查
    if (!connPath0.isEmpty()) {
        NetworkManager::Connection::Ptr conn = NetworkManager::findConnection(connPath0);
        if (!conn) {
            qCWarning(DSM()) << "Connection not found:" << connPath0;
            return QString();
        }
        NetworkManager::activateConnection(connPath0, device->uni(), QString());
        qDebug() << "connected:" << connPath0;
    }
    
    // ... 现有代码 ...
}

3. DeviceManagerRealize 类变更

优点:

  • 简化了设备启用逻辑,移除了复杂的连接处理代码
  • 删除了不再需要的 deviceEnabledAction() 方法

问题:

  1. 错误处理不足:在 setEnabled() 中,没有检查 dbusInter.call() 是否成功。
  2. 逻辑问题:移除了设备启用后的自动连接逻辑,但可能在某些情况下导致设备启用后不自动连接。

改进建议:

void DeviceManagerRealize::setEnabled(bool enabled)
{
    qCDebug(DNC) << QString("set Device %1, enabled: %2").arg(m_device->uni()).arg(enabled ? "true" : "false");
    QDBusInterface dbusInter(SYS_NETWORK_SERVICE, SYS_NETWORK_PATH, SYS_NETWORK_INTER, QDBusConnection::systemBus());
    QDBusReply<QDBusObjectPath> reply = dbusInter.call("EnableDevice", m_device->uni(), enabled);
    
    if (!reply.isValid()) {
        qCWarning(DNC) << "Failed to enable device:" << reply.error().message();
        return;
    }
    
    // 如果启用设备,确保自动连接逻辑在服务端处理
    if (enabled) {
        qCDebug(DNC) << "Device enabled successfully";
    }
}

代码质量评估

  1. 可读性:代码整体结构清晰,但部分方法较长,可以考虑进一步拆分。
  2. 可维护性:删除了冗余代码,提高了可维护性,但错误处理仍需加强。
  3. 可测试性:新增的方法没有明显的单元测试支持,建议添加测试用例。

安全性评估

  1. 输入验证:部分方法缺少输入参数验证,可能导致潜在的安全问题。
  2. 错误处理:多处缺少错误处理,可能导致程序异常行为。
  3. 资源管理:频繁的文件I/O操作可能影响系统性能。

总结与建议

  1. 加强输入参数验证和错误处理,提高代码健壮性。
  2. 优化文件I/O操作,考虑使用定时器延迟保存配置。
  3. 添加单元测试,确保代码的正确性和稳定性。
  4. 考虑添加日志记录,便于问题追踪和调试。
  5. 对于复杂的逻辑,添加注释说明设计意图和实现细节。

这些改进将有助于提高代码的质量、可靠性和可维护性。

@ut003640
Copy link
Contributor Author

ut003640 commented Mar 9, 2026

/merge

@deepin-bot deepin-bot bot merged commit 7bf893c into linuxdeepin:master Mar 9, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants