设备监控

This commit is contained in:
flower_linux
2026-03-19 10:47:26 +08:00
parent 91a3361d1f
commit 903c349e1d
48 changed files with 820 additions and 100 deletions

Binary file not shown.

View File

@@ -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)

Binary file not shown.

View File

@@ -6,6 +6,7 @@
#include <QSaveFile>
#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<monitor::DeviceMonitorService> monitorService;
std::shared_ptr<DeviceTreeModel> 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<monitor::DeviceMonitorService>(nullptr);
if (!d->monitorService->start()) {
LOG_WARNING() << "DeviceBus: device monitor service start failed";
}
}
if (!d->treeModel) {
d->treeModel = std::make_shared<DeviceTreeModel>();
}
@@ -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) {

View File

@@ -1,77 +0,0 @@
#include "device_bus/capabilities/DriverCapabilityProvider.h"
#include <QFile>
#include <QJsonDocument>
#include <QJsonArray>
#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;
}

View File

@@ -1,21 +0,0 @@
#pragma once
#include <QJsonObject>
#include <QString>
// 负责加载 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;
};

View File

@@ -0,0 +1,74 @@
#pragma once
#include <QString>
#include <QDateTime>
#include <QMap>
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<QString, QString> 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

View File

@@ -0,0 +1,97 @@
#include "device_bus/monitor/DeviceMonitorService.h"
#include <QJsonDocument>
#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<UdevDeviceMonitor>(this));
m_monitors.emplace_back(std::make_unique<NetlinkDeviceMonitor>(this));
#elif defined(_WIN32)
m_monitors.emplace_back(std::make_unique<WindowsDeviceMonitor>(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

View File

@@ -0,0 +1,36 @@
#pragma once
#include <QObject>
#include <memory>
#include <vector>
#include <QHash>
#include <QDateTime>
#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<std::unique_ptr<IDeviceMonitor>> m_monitors;
bool m_started{false};
// Lightweight de-dup: key -> last timestamp (ms)
QHash<QString, qint64> m_lastEventMsByKey;
};
} // namespace softbus::device_bus::monitor

View File

@@ -0,0 +1,41 @@
#pragma once
#include <functional>
#include <QObject>
#include "device_bus/monitor/DeviceEvent.h"
namespace softbus::device_bus::monitor
{
class IDeviceMonitor : public QObject
{
Q_OBJECT
public:
using EventCallback = std::function<void(const DeviceEvent&)>;
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

View File

@@ -0,0 +1,155 @@
#include "device_bus/monitor/NetlinkDeviceMonitor.h"
#if defined(__linux__)
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <net/if.h>
#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<sockaddr*>(&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<int>(len);
for (nlmsghdr* nh = reinterpret_cast<nlmsghdr*>(buf);
NLMSG_OK(nh, static_cast<unsigned int>(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<ifinfomsg*>(NLMSG_DATA(nh));
const int ifindex = ifi ? ifi->ifi_index : -1;
QString ifname;
bool linkUp = false;
// Parse attributes
int attrLen = static_cast<int>(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<const char*>(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__

View File

@@ -0,0 +1,38 @@
#pragma once
#include "device_bus/monitor/IDeviceMonitor.h"
#if defined(__linux__)
#include <QSocketNotifier>
#include <QSet>
#include <QHash>
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<int> m_seenIfIndex;
QHash<int, QString> m_lastIfNameByIndex;
QHash<int, bool> m_lastLinkUpByIndex;
};
} // namespace softbus::device_bus::monitor
#endif // __linux__

View File

@@ -0,0 +1,222 @@
#include "device_bus/monitor/UdevDeviceMonitor.h"
#if defined(__linux__)
#include <unistd.h>
#include <QFileInfo>
#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__

View File

@@ -0,0 +1,39 @@
#pragma once
#include "device_bus/monitor/IDeviceMonitor.h"
#if defined(__linux__)
#include <libudev.h>
#include <QSocketNotifier>
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__

View File

@@ -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

View File

@@ -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