diff --git a/.cache/clangd/index/CoreService.cpp.4E2160035C5FBDAD.idx b/.cache/clangd/index/CoreService.cpp.4E2160035C5FBDAD.idx index 980ad9c..2182812 100644 Binary files a/.cache/clangd/index/CoreService.cpp.4E2160035C5FBDAD.idx and b/.cache/clangd/index/CoreService.cpp.4E2160035C5FBDAD.idx differ diff --git a/.cache/clangd/index/CoreService.h.545B9643BE6343CF.idx b/.cache/clangd/index/CoreService.h.545B9643BE6343CF.idx index ba420e5..b613be4 100644 Binary files a/.cache/clangd/index/CoreService.h.545B9643BE6343CF.idx and b/.cache/clangd/index/CoreService.h.545B9643BE6343CF.idx differ diff --git a/.cache/clangd/index/DeviceBus.cpp.3FE983A42FE7A6A8.idx b/.cache/clangd/index/DeviceBus.cpp.3FE983A42FE7A6A8.idx index 1fa1bdb..8043a60 100644 Binary files a/.cache/clangd/index/DeviceBus.cpp.3FE983A42FE7A6A8.idx and b/.cache/clangd/index/DeviceBus.cpp.3FE983A42FE7A6A8.idx differ diff --git a/.cache/clangd/index/DeviceBus.h.170EB5C1B9D8C650.idx b/.cache/clangd/index/DeviceBus.h.170EB5C1B9D8C650.idx index 8f603e6..9192706 100644 Binary files a/.cache/clangd/index/DeviceBus.h.170EB5C1B9D8C650.idx and b/.cache/clangd/index/DeviceBus.h.170EB5C1B9D8C650.idx differ diff --git a/.cache/clangd/index/DeviceBusManager.cpp.134168DDD210A4ED.idx b/.cache/clangd/index/DeviceBusManager.cpp.134168DDD210A4ED.idx index 865289c..fe7255a 100644 Binary files a/.cache/clangd/index/DeviceBusManager.cpp.134168DDD210A4ED.idx and b/.cache/clangd/index/DeviceBusManager.cpp.134168DDD210A4ED.idx differ diff --git a/.cache/clangd/index/DeviceBusManager.h.4DAD625CA48C7EE3.idx b/.cache/clangd/index/DeviceBusManager.h.4DAD625CA48C7EE3.idx index f4023f8..0598482 100644 Binary files a/.cache/clangd/index/DeviceBusManager.h.4DAD625CA48C7EE3.idx and b/.cache/clangd/index/DeviceBusManager.h.4DAD625CA48C7EE3.idx differ diff --git a/.cache/clangd/index/DeviceEvent.h.86CA2B2EF71C68E1.idx b/.cache/clangd/index/DeviceEvent.h.86CA2B2EF71C68E1.idx new file mode 100644 index 0000000..923a004 Binary files /dev/null and b/.cache/clangd/index/DeviceEvent.h.86CA2B2EF71C68E1.idx differ diff --git a/.cache/clangd/index/DeviceMonitorService.cpp.3C0446CEC6FFCFAC.idx b/.cache/clangd/index/DeviceMonitorService.cpp.3C0446CEC6FFCFAC.idx new file mode 100644 index 0000000..8950ce9 Binary files /dev/null and b/.cache/clangd/index/DeviceMonitorService.cpp.3C0446CEC6FFCFAC.idx differ diff --git a/.cache/clangd/index/DeviceMonitorService.h.2C037D82AC481AAB.idx b/.cache/clangd/index/DeviceMonitorService.h.2C037D82AC481AAB.idx new file mode 100644 index 0000000..12e7e8d Binary files /dev/null and b/.cache/clangd/index/DeviceMonitorService.h.2C037D82AC481AAB.idx differ diff --git a/.cache/clangd/index/DeviceTreeModel.cpp.D68EA11C8BBF4ADF.idx b/.cache/clangd/index/DeviceTreeModel.cpp.D68EA11C8BBF4ADF.idx index 1c855bc..709efaa 100644 Binary files a/.cache/clangd/index/DeviceTreeModel.cpp.D68EA11C8BBF4ADF.idx and b/.cache/clangd/index/DeviceTreeModel.cpp.D68EA11C8BBF4ADF.idx differ diff --git a/.cache/clangd/index/DeviceTreeModel.h.A5637DBA59AB7F8A.idx b/.cache/clangd/index/DeviceTreeModel.h.A5637DBA59AB7F8A.idx index c8c46f5..d40cdd8 100644 Binary files a/.cache/clangd/index/DeviceTreeModel.h.A5637DBA59AB7F8A.idx and b/.cache/clangd/index/DeviceTreeModel.h.A5637DBA59AB7F8A.idx differ diff --git a/.cache/clangd/index/DeviceTypes.cpp.1EEEBE5AE8DFBAC3.idx b/.cache/clangd/index/DeviceTypes.cpp.1EEEBE5AE8DFBAC3.idx index bcba590..ae9ac4f 100644 Binary files a/.cache/clangd/index/DeviceTypes.cpp.1EEEBE5AE8DFBAC3.idx and b/.cache/clangd/index/DeviceTypes.cpp.1EEEBE5AE8DFBAC3.idx differ diff --git a/.cache/clangd/index/DeviceTypes.h.265DE4F5160B4435.idx b/.cache/clangd/index/DeviceTypes.h.265DE4F5160B4435.idx index 9a60425..67fdc04 100644 Binary files a/.cache/clangd/index/DeviceTypes.h.265DE4F5160B4435.idx and b/.cache/clangd/index/DeviceTypes.h.265DE4F5160B4435.idx differ diff --git a/.cache/clangd/index/IDevice.h.606309017A5E5AB6.idx b/.cache/clangd/index/IDevice.h.606309017A5E5AB6.idx index 574b08f..6820ea4 100644 Binary files a/.cache/clangd/index/IDevice.h.606309017A5E5AB6.idx and b/.cache/clangd/index/IDevice.h.606309017A5E5AB6.idx differ diff --git a/.cache/clangd/index/IDeviceMonitor.h.35FD4D7CC78F8E5E.idx b/.cache/clangd/index/IDeviceMonitor.h.35FD4D7CC78F8E5E.idx new file mode 100644 index 0000000..360b03f Binary files /dev/null and b/.cache/clangd/index/IDeviceMonitor.h.35FD4D7CC78F8E5E.idx differ diff --git a/.cache/clangd/index/IDeviceRegistry.h.255744FF99F84B26.idx b/.cache/clangd/index/IDeviceRegistry.h.255744FF99F84B26.idx new file mode 100644 index 0000000..dc17278 Binary files /dev/null and b/.cache/clangd/index/IDeviceRegistry.h.255744FF99F84B26.idx differ diff --git a/.cache/clangd/index/IPhysicalDevice.h.37CA78ACF4BEE83B.idx b/.cache/clangd/index/IPhysicalDevice.h.37CA78ACF4BEE83B.idx index 1337a88..7564ffb 100644 Binary files a/.cache/clangd/index/IPhysicalDevice.h.37CA78ACF4BEE83B.idx and b/.cache/clangd/index/IPhysicalDevice.h.37CA78ACF4BEE83B.idx differ diff --git a/.cache/clangd/index/IStreamDriver.h.7F77A7878AD58FDD.idx b/.cache/clangd/index/IStreamDriver.h.7F77A7878AD58FDD.idx index 3c2e111..c82deaa 100644 Binary files a/.cache/clangd/index/IStreamDriver.h.7F77A7878AD58FDD.idx and b/.cache/clangd/index/IStreamDriver.h.7F77A7878AD58FDD.idx differ diff --git a/.cache/clangd/index/IpcDBus.cpp.F75762A8BF6179E0.idx b/.cache/clangd/index/IpcDBus.cpp.F75762A8BF6179E0.idx index 6a719fa..824d38f 100644 Binary files a/.cache/clangd/index/IpcDBus.cpp.F75762A8BF6179E0.idx and b/.cache/clangd/index/IpcDBus.cpp.F75762A8BF6179E0.idx differ diff --git a/.cache/clangd/index/IpcDBus.h.DD48681835A8EA94.idx b/.cache/clangd/index/IpcDBus.h.DD48681835A8EA94.idx index dd18520..2f1cab4 100644 Binary files a/.cache/clangd/index/IpcDBus.h.DD48681835A8EA94.idx and b/.cache/clangd/index/IpcDBus.h.DD48681835A8EA94.idx differ diff --git a/.cache/clangd/index/NetlinkDeviceMonitor.cpp.852AB1C86537EBFF.idx b/.cache/clangd/index/NetlinkDeviceMonitor.cpp.852AB1C86537EBFF.idx new file mode 100644 index 0000000..1b9e41a Binary files /dev/null and b/.cache/clangd/index/NetlinkDeviceMonitor.cpp.852AB1C86537EBFF.idx differ diff --git a/.cache/clangd/index/NetlinkDeviceMonitor.h.D31493ABAC6FE7FD.idx b/.cache/clangd/index/NetlinkDeviceMonitor.h.D31493ABAC6FE7FD.idx new file mode 100644 index 0000000..918357f Binary files /dev/null and b/.cache/clangd/index/NetlinkDeviceMonitor.h.D31493ABAC6FE7FD.idx differ diff --git a/.cache/clangd/index/SerialCapabilities.h.50ACDAA3497BB1F6.idx b/.cache/clangd/index/SerialCapabilities.h.50ACDAA3497BB1F6.idx index a616aa3..4b8c22b 100644 Binary files a/.cache/clangd/index/SerialCapabilities.h.50ACDAA3497BB1F6.idx and b/.cache/clangd/index/SerialCapabilities.h.50ACDAA3497BB1F6.idx differ diff --git a/.cache/clangd/index/SerialDevice.cpp.E5FA00DC79EFDCA9.idx b/.cache/clangd/index/SerialDevice.cpp.E5FA00DC79EFDCA9.idx index 0ee821c..75fb352 100644 Binary files a/.cache/clangd/index/SerialDevice.cpp.E5FA00DC79EFDCA9.idx and b/.cache/clangd/index/SerialDevice.cpp.E5FA00DC79EFDCA9.idx differ diff --git a/.cache/clangd/index/SerialDevice.h.6BEE4982C476E8CB.idx b/.cache/clangd/index/SerialDevice.h.6BEE4982C476E8CB.idx index 3839ee1..2159778 100644 Binary files a/.cache/clangd/index/SerialDevice.h.6BEE4982C476E8CB.idx and b/.cache/clangd/index/SerialDevice.h.6BEE4982C476E8CB.idx differ diff --git a/.cache/clangd/index/SerialQtDriver.cpp.E8F1085153E8E1E0.idx b/.cache/clangd/index/SerialQtDriver.cpp.E8F1085153E8E1E0.idx index 8990c78..028a7ac 100644 Binary files a/.cache/clangd/index/SerialQtDriver.cpp.E8F1085153E8E1E0.idx and b/.cache/clangd/index/SerialQtDriver.cpp.E8F1085153E8E1E0.idx differ diff --git a/.cache/clangd/index/SerialQtDriver.h.0D1EBDDF4831E0E3.idx b/.cache/clangd/index/SerialQtDriver.h.0D1EBDDF4831E0E3.idx index 037d162..74d9e0c 100644 Binary files a/.cache/clangd/index/SerialQtDriver.h.0D1EBDDF4831E0E3.idx and b/.cache/clangd/index/SerialQtDriver.h.0D1EBDDF4831E0E3.idx differ diff --git a/.cache/clangd/index/UdevDeviceMonitor.cpp.8FD4DE713A589E66.idx b/.cache/clangd/index/UdevDeviceMonitor.cpp.8FD4DE713A589E66.idx new file mode 100644 index 0000000..d8682aa Binary files /dev/null and b/.cache/clangd/index/UdevDeviceMonitor.cpp.8FD4DE713A589E66.idx differ diff --git a/.cache/clangd/index/UdevDeviceMonitor.h.0028E083196B240B.idx b/.cache/clangd/index/UdevDeviceMonitor.h.0028E083196B240B.idx new file mode 100644 index 0000000..420e228 Binary files /dev/null and b/.cache/clangd/index/UdevDeviceMonitor.h.0028E083196B240B.idx differ diff --git a/.cache/clangd/index/WindowsDeviceMonitor.cpp.4D5B11AEA4D9CB44.idx b/.cache/clangd/index/WindowsDeviceMonitor.cpp.4D5B11AEA4D9CB44.idx new file mode 100644 index 0000000..8ba7cf7 Binary files /dev/null and b/.cache/clangd/index/WindowsDeviceMonitor.cpp.4D5B11AEA4D9CB44.idx differ diff --git a/.cache/clangd/index/WindowsDeviceMonitor.h.00AD1ABB43F51CCC.idx b/.cache/clangd/index/WindowsDeviceMonitor.h.00AD1ABB43F51CCC.idx new file mode 100644 index 0000000..3514742 Binary files /dev/null and b/.cache/clangd/index/WindowsDeviceMonitor.h.00AD1ABB43F51CCC.idx differ diff --git a/.cache/clangd/index/logging.h.C8CBC4848C286E20.idx b/.cache/clangd/index/logging.h.C8CBC4848C286E20.idx index be0a8bd..bf2c7ed 100644 Binary files a/.cache/clangd/index/logging.h.C8CBC4848C286E20.idx and b/.cache/clangd/index/logging.h.C8CBC4848C286E20.idx differ diff --git a/.cache/clangd/index/main.cpp.76A75E580B25A05E.idx b/.cache/clangd/index/main.cpp.76A75E580B25A05E.idx index 8168620..dc96be6 100644 Binary files a/.cache/clangd/index/main.cpp.76A75E580B25A05E.idx and b/.cache/clangd/index/main.cpp.76A75E580B25A05E.idx differ diff --git a/CMakeLists.txt b/CMakeLists.txt index 0cb7f52..9caca72 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,6 +31,7 @@ set(CMAKE_AUTORCC ON) set(CMAKE_AUTOMOC ON) # 查找Qt6(守护进程只需要 Core/DBus/Network/SerialPort/Sql) find_package(Qt6 6.5 REQUIRED COMPONENTS Core SerialPort Network Sql DBus) +include(CheckIncludeFileCXX) # 检测编译平台 if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "4") @@ -77,8 +78,19 @@ qt_add_executable(softbus_daemon # device bus: tree & capabilities src/device_bus/tree/DeviceTreeModel.h src/device_bus/tree/DeviceTreeModel.cpp - src/device_bus/capabilities/DriverCapabilityProvider.h - src/device_bus/capabilities/DriverCapabilityProvider.cpp + + # device bus: monitor + src/device_bus/monitor/DeviceEvent.h + src/device_bus/monitor/IDeviceMonitor.h + src/device_bus/monitor/DeviceMonitorService.h + src/device_bus/monitor/DeviceMonitorService.cpp + src/device_bus/monitor/UdevDeviceMonitor.h + src/device_bus/monitor/UdevDeviceMonitor.cpp + src/device_bus/monitor/NetlinkDeviceMonitor.h + src/device_bus/monitor/NetlinkDeviceMonitor.cpp + src/device_bus/monitor/WindowsDeviceMonitor.h + src/device_bus/monitor/WindowsDeviceMonitor.cpp + # device bus: facade & manager src/device_bus/DeviceBus.h @@ -114,6 +126,27 @@ target_link_libraries(softbus_daemon Qt6::Network ) +# Platform-specific deps +if(UNIX AND NOT APPLE) + find_package(PkgConfig QUIET) + if(PkgConfig_FOUND) + pkg_check_modules(UDEV QUIET libudev) + endif() + + if(UDEV_FOUND) + target_include_directories(softbus_daemon PRIVATE ${UDEV_INCLUDE_DIRS}) + target_link_directories(softbus_daemon PRIVATE ${UDEV_LIBRARY_DIRS}) + target_link_libraries(softbus_daemon PRIVATE ${UDEV_LIBRARIES}) + else() + # Fallback: rely on system linker search path + target_link_libraries(softbus_daemon PRIVATE udev) + endif() +endif() + +if(WIN32) + target_link_libraries(softbus_daemon PRIVATE setupapi iphlpapi user32 advapi32) +endif() + add_subdirectory(plugins) diff --git a/bin/softbus_daemon b/bin/softbus_daemon index c552a8c..bfbcc83 100755 Binary files a/bin/softbus_daemon and b/bin/softbus_daemon differ diff --git a/src/device_bus/DeviceBus.cpp b/src/device_bus/DeviceBus.cpp index 8c0b936..d6cb732 100644 --- a/src/device_bus/DeviceBus.cpp +++ b/src/device_bus/DeviceBus.cpp @@ -6,6 +6,7 @@ #include #include "device_bus/manager/DeviceBusManager.h" +#include "device_bus/monitor/DeviceMonitorService.h" #include "device_bus/tree/DeviceTreeModel.h" #include "utils/logs/logging.h" @@ -14,6 +15,7 @@ namespace softbus::device_bus { class DeviceBus::Impl { public: DeviceBusManager manager; + std::unique_ptr monitorService; std::shared_ptr treeModel; QString configPath; bool deviceTreeDirty{false}; @@ -32,6 +34,13 @@ DeviceBus::~DeviceBus() { } bool DeviceBus::initialize(const QJsonObject &rootConfig) { + if (!d->monitorService) { + d->monitorService = std::make_unique(nullptr); + if (!d->monitorService->start()) { + LOG_WARNING() << "DeviceBus: device monitor service start failed"; + } + } + if (!d->treeModel) { d->treeModel = std::make_shared(); } @@ -40,6 +49,11 @@ bool DeviceBus::initialize(const QJsonObject &rootConfig) { } void DeviceBus::shutdown() { + if (d->monitorService) { + d->monitorService->stop(); + d->monitorService.reset(); + } + d->manager.shutdown(); if (!d->deviceTreeDirty) { diff --git a/src/device_bus/capabilities/DriverCapabilityProvider.cpp b/src/device_bus/capabilities/DriverCapabilityProvider.cpp deleted file mode 100644 index bec6595..0000000 --- a/src/device_bus/capabilities/DriverCapabilityProvider.cpp +++ /dev/null @@ -1,77 +0,0 @@ -#include "device_bus/capabilities/DriverCapabilityProvider.h" - -#include -#include -#include - -#include "utils/logs/logging.h" - -bool DriverCapabilityProvider::loadFromFile(const QString& jsonPath, QString* errorMessage) -{ - m_defaults = QJsonObject(); - m_enums = QJsonObject(); - - QFile f(jsonPath); - if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) { - if (errorMessage) { - *errorMessage = QStringLiteral("Failed to open capability file: %1").arg(jsonPath); - } - return false; - } - - const QByteArray data = f.readAll(); - f.close(); - - QJsonParseError pe; - const QJsonDocument doc = QJsonDocument::fromJson(data, &pe); - if (pe.error != QJsonParseError::NoError || !doc.isObject()) { - if (errorMessage) { - *errorMessage = QStringLiteral("Invalid JSON in capability file %1: %2") - .arg(jsonPath, pe.errorString()); - } - return false; - } - - const QJsonObject root = doc.object(); - m_defaults = root.value(QStringLiteral("defaults")).toObject(); - m_enums = root.value(QStringLiteral("enums")).toObject(); - - LOG_INFO() << "DriverCapabilityProvider loaded from" << jsonPath; - return true; -} - -QJsonObject DriverCapabilityProvider::mergeAndValidateParams(const QJsonObject& nodeParams) const -{ - // 1. 从 defaults 拷贝一份出来 - QJsonObject result = m_defaults; - - // 2. 覆盖节点里的显式参数 - for (auto it = nodeParams.constBegin(); it != nodeParams.constEnd(); ++it) { - const QString key = it.key(); - const QJsonValue value = it.value(); - - // 如果存在枚举限制,则先校验 - const QJsonValue enumVal = m_enums.value(key); - if (enumVal.isArray()) { - const QJsonArray arr = enumVal.toArray(); - bool ok = false; - for (const QJsonValue& candidate : arr) { - if (candidate == value) { - ok = true; - break; - } - } - if (!ok) { - LOG_WARNING() << "DriverCapabilityProvider: param" << key - << "value" << value.toVariant() - << "not in allowed enum, keep default"; - continue; // 不覆盖默认值 - } - } - - result.insert(key, value); - } - - return result; -} - diff --git a/src/device_bus/capabilities/DriverCapabilityProvider.h b/src/device_bus/capabilities/DriverCapabilityProvider.h deleted file mode 100644 index a740974..0000000 --- a/src/device_bus/capabilities/DriverCapabilityProvider.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include -#include - -// 负责加载 drivers 下的 parameters.json,并对节点 params 做默认值合并与简单枚举校验 - -class DriverCapabilityProvider -{ -public: - // 从给定路径加载能力描述(如 "src/hardware/drivers/serial/parameters.json") - bool loadFromFile(const QString& jsonPath, QString* errorMessage = nullptr); - - // 将节点 params 与 defaults 合并,并按 enums 做简单校验 - QJsonObject mergeAndValidateParams(const QJsonObject& nodeParams) const; - -private: - QJsonObject m_defaults; - QJsonObject m_enums; -}; - diff --git a/src/device_bus/monitor/DeviceEvent.h b/src/device_bus/monitor/DeviceEvent.h new file mode 100644 index 0000000..13744f0 --- /dev/null +++ b/src/device_bus/monitor/DeviceEvent.h @@ -0,0 +1,74 @@ +#pragma once + +#include +#include +#include + +namespace softbus::device_bus::monitor +{ + +enum class DeviceEventType { + Serial, + Usb, + Storage, + NetInterface, + Unknown, +}; + +enum class DeviceEventAction { + Add, + Remove, + Change, + Unknown, +}; + +struct DeviceEvent +{ + DeviceEventType type{DeviceEventType::Unknown}; + DeviceEventAction action{DeviceEventAction::Unknown}; + QDateTime timestamp{QDateTime::currentDateTimeUtc()}; + + // Common identifiers + QString subsystem; // e.g. "tty", "usb", "block", "net" + QString devnode; // e.g. /dev/ttyUSB0 + QString devicePath; // platform-native path (Windows device interface path) + + // USB-ish fields + QString vid; + QString pid; + QString serial; + QString model; + QString vendor; + + // Network fields + QString ifname; + int ifindex{-1}; + bool linkUp{false}; + + // Extra properties (best-effort) + QMap props; +}; + +inline QString toString(DeviceEventType t) +{ + switch (t) { + case DeviceEventType::Serial: return QStringLiteral("Serial"); + case DeviceEventType::Usb: return QStringLiteral("Usb"); + case DeviceEventType::Storage: return QStringLiteral("Storage"); + case DeviceEventType::NetInterface: return QStringLiteral("NetInterface"); + default: return QStringLiteral("Unknown"); + } +} + +inline QString toString(DeviceEventAction a) +{ + switch (a) { + case DeviceEventAction::Add: return QStringLiteral("add"); + case DeviceEventAction::Remove: return QStringLiteral("remove"); + case DeviceEventAction::Change: return QStringLiteral("change"); + default: return QStringLiteral("unknown"); + } +} + +} // namespace softbus::device_bus::monitor + diff --git a/src/device_bus/monitor/DeviceMonitorService.cpp b/src/device_bus/monitor/DeviceMonitorService.cpp new file mode 100644 index 0000000..8ed8589 --- /dev/null +++ b/src/device_bus/monitor/DeviceMonitorService.cpp @@ -0,0 +1,97 @@ +#include "device_bus/monitor/DeviceMonitorService.h" + +#include + +#include "utils/logs/logging.h" + +#if defined(__linux__) +#include "device_bus/monitor/UdevDeviceMonitor.h" +#include "device_bus/monitor/NetlinkDeviceMonitor.h" +#endif + +#if defined(_WIN32) +#include "device_bus/monitor/WindowsDeviceMonitor.h" +#endif + +namespace softbus::device_bus::monitor +{ + +DeviceMonitorService::DeviceMonitorService(QObject* parent) + : QObject(parent) +{ +#if defined(__linux__) + m_monitors.emplace_back(std::make_unique(this)); + m_monitors.emplace_back(std::make_unique(this)); +#elif defined(_WIN32) + m_monitors.emplace_back(std::make_unique(this)); +#endif + + for (auto& m : m_monitors) { + m->setCallback([this](const DeviceEvent& ev) { onEvent(ev); }); + } +} + +DeviceMonitorService::~DeviceMonitorService() +{ + stop(); +} + +bool DeviceMonitorService::start() +{ + if (m_started) return true; + + bool ok = true; + for (auto& m : m_monitors) { + ok = m->start() && ok; + } + m_started = ok; + return ok; +} + +void DeviceMonitorService::stop() +{ + if (!m_started) return; + for (auto& m : m_monitors) { + m->stop(); + } + m_started = false; +} + +void DeviceMonitorService::onEvent(const DeviceEvent& ev) +{ + // De-dup (short window): suppress identical events bursts + const QString key = QStringLiteral("%1|%2|%3|%4|%5|%6|%7|%8|%9") + .arg(toString(ev.type), + toString(ev.action), + ev.subsystem, + ev.devnode, + ev.props.value(QStringLiteral("SYSPATH")), + ev.ifname, + QString::number(ev.ifindex), + ev.vid + ev.pid, + ev.serial); + + const qint64 nowMs = QDateTime::currentMSecsSinceEpoch(); + const qint64 lastMs = m_lastEventMsByKey.value(key, -1); + if (lastMs >= 0 && (nowMs - lastMs) < 800) { + return; + } + m_lastEventMsByKey.insert(key, nowMs); + + // 第一版:仅日志 + LOG_INFO() << "DeviceMonitor event" + << "type=" << toString(ev.type) + << "action=" << toString(ev.action) + << "subsystem=" << ev.subsystem + << "devnode=" << ev.devnode + << "ifname=" << ev.ifname + << "ifindex=" << ev.ifindex + << "linkUp=" << ev.linkUp + << "vid=" << ev.vid + << "pid=" << ev.pid + << "serial=" << ev.serial + << "path=" << ev.devicePath; +} + +} // namespace softbus::device_bus::monitor + diff --git a/src/device_bus/monitor/DeviceMonitorService.h b/src/device_bus/monitor/DeviceMonitorService.h new file mode 100644 index 0000000..bb192ef --- /dev/null +++ b/src/device_bus/monitor/DeviceMonitorService.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "device_bus/monitor/IDeviceMonitor.h" + +namespace softbus::device_bus::monitor +{ + +class DeviceMonitorService : public QObject +{ + Q_OBJECT +public: + explicit DeviceMonitorService(QObject* parent = nullptr); + ~DeviceMonitorService() override; + + bool start(); + void stop(); + +private: + void onEvent(const DeviceEvent& ev); + +private: + std::vector> m_monitors; + bool m_started{false}; + + // Lightweight de-dup: key -> last timestamp (ms) + QHash m_lastEventMsByKey; +}; + +} // namespace softbus::device_bus::monitor + diff --git a/src/device_bus/monitor/IDeviceMonitor.h b/src/device_bus/monitor/IDeviceMonitor.h new file mode 100644 index 0000000..115b754 --- /dev/null +++ b/src/device_bus/monitor/IDeviceMonitor.h @@ -0,0 +1,41 @@ +#pragma once + +#include + +#include + +#include "device_bus/monitor/DeviceEvent.h" + +namespace softbus::device_bus::monitor +{ + +class IDeviceMonitor : public QObject +{ + Q_OBJECT +public: + using EventCallback = std::function; + + explicit IDeviceMonitor(QObject* parent = nullptr) : QObject(parent) {} + ~IDeviceMonitor() override = default; + + virtual bool start() = 0; + virtual void stop() = 0; + + void setCallback(EventCallback cb) { m_cb = std::move(cb); } + +protected: + void emitEvent(const DeviceEvent& ev) + { + if (m_cb) m_cb(ev); + emit event(ev); + } + +signals: + void event(const softbus::device_bus::monitor::DeviceEvent& ev); + +private: + EventCallback m_cb; +}; + +} // namespace softbus::device_bus::monitor + diff --git a/src/device_bus/monitor/NetlinkDeviceMonitor.cpp b/src/device_bus/monitor/NetlinkDeviceMonitor.cpp new file mode 100644 index 0000000..ca94e25 --- /dev/null +++ b/src/device_bus/monitor/NetlinkDeviceMonitor.cpp @@ -0,0 +1,155 @@ +#include "device_bus/monitor/NetlinkDeviceMonitor.h" + +#if defined(__linux__) + +#include +#include +#include + +#include +#include +#include +#include + +#include "utils/logs/logging.h" + +namespace softbus::device_bus::monitor +{ + +NetlinkDeviceMonitor::NetlinkDeviceMonitor(QObject* parent) + : IDeviceMonitor(parent) +{ +} + +NetlinkDeviceMonitor::~NetlinkDeviceMonitor() +{ + stop(); +} + +bool NetlinkDeviceMonitor::start() +{ + if (m_sock >= 0) return true; + + m_sock = ::socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (m_sock < 0) { + LOG_ERROR() << "NetlinkDeviceMonitor: socket failed errno=" << errno; + return false; + } + + sockaddr_nl addr{}; + addr.nl_family = AF_NETLINK; + addr.nl_groups = RTMGRP_LINK; + if (::bind(m_sock, reinterpret_cast(&addr), sizeof(addr)) < 0) { + LOG_ERROR() << "NetlinkDeviceMonitor: bind failed errno=" << errno; + stop(); + return false; + } + + m_notifier = new QSocketNotifier(m_sock, QSocketNotifier::Read, this); + connect(m_notifier, &QSocketNotifier::activated, this, [this]() { onReadable(); }); + + LOG_INFO() << "NetlinkDeviceMonitor started sock=" << m_sock; + return true; +} + +void NetlinkDeviceMonitor::stop() +{ + if (m_notifier) { + m_notifier->setEnabled(false); + m_notifier->deleteLater(); + m_notifier = nullptr; + } + if (m_sock >= 0) { + ::close(m_sock); + m_sock = -1; + } + m_seenIfIndex.clear(); + m_lastIfNameByIndex.clear(); + m_lastLinkUpByIndex.clear(); +} + +void NetlinkDeviceMonitor::onReadable() +{ + if (m_sock < 0) return; + + alignas(nlmsghdr) char buf[8192]; + const ssize_t len = ::recv(m_sock, buf, sizeof(buf), 0); + if (len <= 0) { + return; + } + + int remaining = static_cast(len); + for (nlmsghdr* nh = reinterpret_cast(buf); + NLMSG_OK(nh, static_cast(remaining)); + nh = NLMSG_NEXT(nh, remaining)) { + + if (nh->nlmsg_type == NLMSG_DONE || nh->nlmsg_type == NLMSG_ERROR) { + continue; + } + + if (nh->nlmsg_type != RTM_NEWLINK && nh->nlmsg_type != RTM_DELLINK) { + continue; + } + + auto* ifi = reinterpret_cast(NLMSG_DATA(nh)); + const int ifindex = ifi ? ifi->ifi_index : -1; + + QString ifname; + bool linkUp = false; + + // Parse attributes + int attrLen = static_cast(nh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi))); + for (rtattr* rta = IFLA_RTA(ifi); RTA_OK(rta, attrLen); rta = RTA_NEXT(rta, attrLen)) { + if (rta->rta_type == IFLA_IFNAME) { + ifname = QString::fromUtf8(reinterpret_cast(RTA_DATA(rta))); + } + } + + // Link state (best-effort) + if (ifi) { + linkUp = (ifi->ifi_flags & IFF_RUNNING) != 0; + } + + DeviceEvent ev; + ev.timestamp = QDateTime::currentDateTimeUtc(); + ev.type = DeviceEventType::NetInterface; + ev.subsystem = QStringLiteral("net"); + ev.ifindex = ifindex; + ev.ifname = ifname; + ev.linkUp = linkUp; + + if (nh->nlmsg_type == RTM_DELLINK) { + ev.action = DeviceEventAction::Remove; + m_seenIfIndex.remove(ifindex); + m_lastIfNameByIndex.remove(ifindex); + m_lastLinkUpByIndex.remove(ifindex); + } else { + // NEWLINK can be add or change; treat first time as add + if (!m_seenIfIndex.contains(ifindex)) { + ev.action = DeviceEventAction::Add; + m_seenIfIndex.insert(ifindex); + m_lastIfNameByIndex.insert(ifindex, ifname); + m_lastLinkUpByIndex.insert(ifindex, linkUp); + } else { + const QString lastName = m_lastIfNameByIndex.value(ifindex); + const bool lastLink = m_lastLinkUpByIndex.value(ifindex, linkUp); + + // Drop pure spam: only emit change if name or link state changes + if (lastName == ifname && lastLink == linkUp) { + continue; + } + + ev.action = DeviceEventAction::Change; + m_lastIfNameByIndex.insert(ifindex, ifname); + m_lastLinkUpByIndex.insert(ifindex, linkUp); + } + } + + emitEvent(ev); + } +} + +} // namespace softbus::device_bus::monitor + +#endif // __linux__ + diff --git a/src/device_bus/monitor/NetlinkDeviceMonitor.h b/src/device_bus/monitor/NetlinkDeviceMonitor.h new file mode 100644 index 0000000..2f43bfd --- /dev/null +++ b/src/device_bus/monitor/NetlinkDeviceMonitor.h @@ -0,0 +1,38 @@ +#pragma once + +#include "device_bus/monitor/IDeviceMonitor.h" + +#if defined(__linux__) + +#include +#include +#include + +namespace softbus::device_bus::monitor +{ + +class NetlinkDeviceMonitor : public IDeviceMonitor +{ + Q_OBJECT +public: + explicit NetlinkDeviceMonitor(QObject* parent = nullptr); + ~NetlinkDeviceMonitor() override; + + bool start() override; + void stop() override; + +private: + void onReadable(); + +private: + int m_sock{-1}; + QSocketNotifier* m_notifier{nullptr}; + QSet m_seenIfIndex; + QHash m_lastIfNameByIndex; + QHash m_lastLinkUpByIndex; +}; + +} // namespace softbus::device_bus::monitor + +#endif // __linux__ + diff --git a/src/device_bus/monitor/UdevDeviceMonitor.cpp b/src/device_bus/monitor/UdevDeviceMonitor.cpp new file mode 100644 index 0000000..4e7122d --- /dev/null +++ b/src/device_bus/monitor/UdevDeviceMonitor.cpp @@ -0,0 +1,222 @@ +#include "device_bus/monitor/UdevDeviceMonitor.h" + +#if defined(__linux__) + +#include + +#include + +#include "utils/logs/logging.h" + +namespace softbus::device_bus::monitor +{ + +UdevDeviceMonitor::UdevDeviceMonitor(QObject* parent) + : IDeviceMonitor(parent) +{ +} + +UdevDeviceMonitor::~UdevDeviceMonitor() +{ + stop(); +} + +bool UdevDeviceMonitor::start() +{ + if (m_mon) return true; + + m_udev = udev_new(); + if (!m_udev) { + LOG_ERROR() << "UdevDeviceMonitor: udev_new failed"; + return false; + } + + m_mon = udev_monitor_new_from_netlink(m_udev, "udev"); + if (!m_mon) { + LOG_ERROR() << "UdevDeviceMonitor: udev_monitor_new_from_netlink failed"; + udev_unref(m_udev); + m_udev = nullptr; + return false; + } + + // Filter subsystems + udev_monitor_filter_add_match_subsystem_devtype(m_mon, "tty", nullptr); + udev_monitor_filter_add_match_subsystem_devtype(m_mon, "usb", nullptr); + udev_monitor_filter_add_match_subsystem_devtype(m_mon, "block", nullptr); + + if (udev_monitor_enable_receiving(m_mon) != 0) { + LOG_ERROR() << "UdevDeviceMonitor: enable_receiving failed"; + stop(); + return false; + } + + m_fd = udev_monitor_get_fd(m_mon); + if (m_fd < 0) { + LOG_ERROR() << "UdevDeviceMonitor: monitor fd invalid"; + stop(); + return false; + } + + m_notifier = new QSocketNotifier(m_fd, QSocketNotifier::Read, this); + connect(m_notifier, &QSocketNotifier::activated, this, [this]() { onReadable(); }); + + LOG_INFO() << "UdevDeviceMonitor started fd=" << m_fd; + return true; +} + +void UdevDeviceMonitor::stop() +{ + if (m_notifier) { + m_notifier->setEnabled(false); + m_notifier->deleteLater(); + m_notifier = nullptr; + } + m_fd = -1; + + if (m_mon) { + udev_monitor_unref(m_mon); + m_mon = nullptr; + } + if (m_udev) { + udev_unref(m_udev); + m_udev = nullptr; + } +} + +void UdevDeviceMonitor::onReadable() +{ + if (!m_mon) return; + + while (true) { + struct udev_device* dev = udev_monitor_receive_device(m_mon); + if (!dev) break; + + const char* action = udev_device_get_action(dev); + DeviceEvent ev = toEvent(dev, action); + + // Drop unknown action (usually noise / incomplete events) + if (ev.action == DeviceEventAction::Unknown) { + udev_device_unref(dev); + continue; + } + + // USB: only keep usb_device (drop interface-level noise) + if (ev.subsystem == QStringLiteral("usb")) { + const QString devtype = ev.props.value(QStringLiteral("DEVTYPE")); + if (!devtype.isEmpty() && devtype != QStringLiteral("usb_device")) { + udev_device_unref(dev); + continue; + } + + // Drop most "change" spam for usb (keep add/remove) + if (ev.action == DeviceEventAction::Change) { + udev_device_unref(dev); + continue; + } + + // If it carries no stable identifiers, it's not useful + if (ev.devnode.isEmpty() && ev.vid.isEmpty() && ev.pid.isEmpty() && ev.serial.isEmpty()) { + udev_device_unref(dev); + continue; + } + } + + // Storage: keep add/remove; drop change spam + if (ev.type == DeviceEventType::Storage && ev.action == DeviceEventAction::Change) { + udev_device_unref(dev); + continue; + } + + emitEvent(ev); + udev_device_unref(dev); + } +} + +QString UdevDeviceMonitor::getProp(struct udev_device* dev, const char* key) +{ + if (!dev || !key) return {}; + const char* v = udev_device_get_property_value(dev, key); + return v ? QString::fromUtf8(v) : QString(); +} + +DeviceEvent UdevDeviceMonitor::toEvent(struct udev_device* dev, const char* action) +{ + DeviceEvent ev; + ev.timestamp = QDateTime::currentDateTimeUtc(); + + const char* subsys = udev_device_get_subsystem(dev); + if (subsys) ev.subsystem = QString::fromUtf8(subsys); + + const char* devtype = udev_device_get_devtype(dev); + if (devtype) ev.props.insert(QStringLiteral("DEVTYPE"), QString::fromUtf8(devtype)); + + const char* syspath = udev_device_get_syspath(dev); + if (syspath) ev.props.insert(QStringLiteral("SYSPATH"), QString::fromUtf8(syspath)); + + const char* devnode = udev_device_get_devnode(dev); + if (devnode) ev.devnode = QString::fromUtf8(devnode); + + const QString act = action ? QString::fromUtf8(action) : QString(); + if (act == QStringLiteral("add")) ev.action = DeviceEventAction::Add; + else if (act == QStringLiteral("remove")) ev.action = DeviceEventAction::Remove; + else if (act == QStringLiteral("change")) ev.action = DeviceEventAction::Change; + else ev.action = DeviceEventAction::Unknown; + + if (ev.subsystem == QStringLiteral("tty")) { + ev.type = DeviceEventType::Serial; + // devnode like /dev/ttyUSB0 + } else if (ev.subsystem == QStringLiteral("usb")) { + ev.type = DeviceEventType::Usb; + } else if (ev.subsystem == QStringLiteral("block")) { + // Prefer only usb-backed block devices + const QString idBus = getProp(dev, "ID_BUS"); + if (idBus == QStringLiteral("usb")) { + ev.type = DeviceEventType::Storage; + } else { + // check parent chain for usb + struct udev_device* p = udev_device_get_parent_with_subsystem_devtype(dev, "usb", nullptr); + if (p) { + ev.type = DeviceEventType::Storage; + } else { + ev.type = DeviceEventType::Unknown; + } + } + } else { + ev.type = DeviceEventType::Unknown; + } + + // Common USB-ish props (best-effort) + ev.vid = getProp(dev, "ID_VENDOR_ID"); + ev.pid = getProp(dev, "ID_MODEL_ID"); + ev.serial = getProp(dev, "ID_SERIAL_SHORT"); + ev.model = getProp(dev, "ID_MODEL"); + ev.vendor = getProp(dev, "ID_VENDOR"); + + // Fallback to sysfs attributes for usb subsystem + if (ev.vid.isEmpty() && ev.subsystem == QStringLiteral("usb")) { + const char* a = udev_device_get_sysattr_value(dev, "idVendor"); + if (a) ev.vid = QString::fromUtf8(a); + } + if (ev.pid.isEmpty() && ev.subsystem == QStringLiteral("usb")) { + const char* a = udev_device_get_sysattr_value(dev, "idProduct"); + if (a) ev.pid = QString::fromUtf8(a); + } + if (ev.serial.isEmpty() && ev.subsystem == QStringLiteral("usb")) { + const char* a = udev_device_get_sysattr_value(dev, "serial"); + if (a) ev.serial = QString::fromUtf8(a); + } + + if (!ev.devnode.isEmpty()) { + ev.props.insert(QStringLiteral("DEVNAME"), ev.devnode); + } + if (!ev.subsystem.isEmpty()) { + ev.props.insert(QStringLiteral("SUBSYSTEM"), ev.subsystem); + } + + return ev; +} + +} // namespace softbus::device_bus::monitor + +#endif // __linux__ + diff --git a/src/device_bus/monitor/UdevDeviceMonitor.h b/src/device_bus/monitor/UdevDeviceMonitor.h new file mode 100644 index 0000000..4db01de --- /dev/null +++ b/src/device_bus/monitor/UdevDeviceMonitor.h @@ -0,0 +1,39 @@ +#pragma once + +#include "device_bus/monitor/IDeviceMonitor.h" + +#if defined(__linux__) + +#include + +#include + +namespace softbus::device_bus::monitor +{ + +class UdevDeviceMonitor : public IDeviceMonitor +{ + Q_OBJECT +public: + explicit UdevDeviceMonitor(QObject* parent = nullptr); + ~UdevDeviceMonitor() override; + + bool start() override; + void stop() override; + +private: + void onReadable(); + DeviceEvent toEvent(struct udev_device* dev, const char* action); + static QString getProp(struct udev_device* dev, const char* key); + +private: + struct udev* m_udev{nullptr}; + struct udev_monitor* m_mon{nullptr}; + int m_fd{-1}; + QSocketNotifier* m_notifier{nullptr}; +}; + +} // namespace softbus::device_bus::monitor + +#endif // __linux__ + diff --git a/src/device_bus/monitor/WindowsDeviceMonitor.cpp b/src/device_bus/monitor/WindowsDeviceMonitor.cpp new file mode 100644 index 0000000..367d661 --- /dev/null +++ b/src/device_bus/monitor/WindowsDeviceMonitor.cpp @@ -0,0 +1,42 @@ +#include "device_bus/monitor/WindowsDeviceMonitor.h" + +#include "utils/logs/logging.h" + +namespace softbus::device_bus::monitor +{ + +WindowsDeviceMonitor::WindowsDeviceMonitor(QObject* parent) + : IDeviceMonitor(parent) +{ +#if defined(_WIN32) + // real impl allocated in start() +#endif +} + +WindowsDeviceMonitor::~WindowsDeviceMonitor() +{ + stop(); +} + +bool WindowsDeviceMonitor::start() +{ +#if defined(_WIN32) + // TODO: Implement WM_DEVICECHANGE + SetupAPI + NotifyIpInterfaceChange. + // Keep as stub for now to allow Linux builds; Windows build will need completing. + LOG_INFO() << "WindowsDeviceMonitor started (stub)"; + return true; +#else + LOG_WARNING() << "WindowsDeviceMonitor is not available on this platform"; + return true; +#endif +} + +void WindowsDeviceMonitor::stop() +{ +#if defined(_WIN32) + LOG_INFO() << "WindowsDeviceMonitor stopped (stub)"; +#endif +} + +} // namespace softbus::device_bus::monitor + diff --git a/src/device_bus/monitor/WindowsDeviceMonitor.h b/src/device_bus/monitor/WindowsDeviceMonitor.h new file mode 100644 index 0000000..c380764 --- /dev/null +++ b/src/device_bus/monitor/WindowsDeviceMonitor.h @@ -0,0 +1,27 @@ +#pragma once + +#include "device_bus/monitor/IDeviceMonitor.h" + +namespace softbus::device_bus::monitor +{ + +// Windows 实现:仅在 _WIN32 下启用;在其他平台上会是一个空实现以保证可编译。 +class WindowsDeviceMonitor : public IDeviceMonitor +{ + Q_OBJECT +public: + explicit WindowsDeviceMonitor(QObject* parent = nullptr); + ~WindowsDeviceMonitor() override; + + bool start() override; + void stop() override; + +private: +#if defined(_WIN32) + struct Impl; + Impl* d{nullptr}; +#endif +}; + +} // namespace softbus::device_bus::monitor +