设备监控-过滤
This commit is contained in:
@@ -52,15 +52,27 @@ bool DeviceBus::initialize(const QJsonObject &rootConfig) {
|
||||
d->manager->setDeviceTreeModel(d->treeModel);
|
||||
d->manager->setRegistry(d->registry.get());
|
||||
|
||||
// 1) Load persisted config and initialize manager
|
||||
if (!d->manager->initialize(rootConfig)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2) Start monitor service
|
||||
if (!d->monitorService) {
|
||||
d->monitorService = std::make_unique<monitor::DeviceMonitorService>(nullptr);
|
||||
d->monitorService->setRegistry(d->registry.get());
|
||||
d->monitorService->setDirtyCallback([this]() { this->markDeviceTreeDirty(); });
|
||||
if (!d->monitorService->start()) {
|
||||
LOG_WARNING() << "DeviceBus: device monitor service start failed";
|
||||
}
|
||||
}
|
||||
return d->manager->initialize(rootConfig);
|
||||
if (!d->monitorService->start()) {
|
||||
LOG_WARNING() << "DeviceBus: device monitor service start failed";
|
||||
}
|
||||
|
||||
// 3) Initial snapshot scan (register existing devices)
|
||||
d->monitorService->initialScan();
|
||||
|
||||
// 4) Reconcile runtime instances against latest tree model
|
||||
d->manager->reconcileFromTreeModel();
|
||||
return true;
|
||||
}
|
||||
|
||||
void DeviceBus::shutdown() {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QSerialPort>
|
||||
#include <QSet>
|
||||
// #include <algorithm>
|
||||
|
||||
#include "hardware/drivers/serial/SerialQtDriver.h"
|
||||
@@ -58,6 +59,44 @@ bool DeviceBusManager::initialize(const QJsonObject& rootConfig)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeviceBusManager::reconcileFromTreeModel()
|
||||
{
|
||||
if (!m_treeModel) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QSet<QString> desiredSerialEndpoints;
|
||||
const auto nodes = m_treeModel->nodes();
|
||||
for (const auto& n : nodes) {
|
||||
if (!n.enabled) continue;
|
||||
if (!n.params.value(QStringLiteral("online")).toBool(true)) continue;
|
||||
|
||||
auto kind = softbus::devices::kindFromType(n.type);
|
||||
if (kind == softbus::devices::DeviceKind::Unknown) {
|
||||
kind = softbus::devices::inferDeviceKindFromEndpoint(n.endpoint);
|
||||
}
|
||||
|
||||
if (kind == softbus::devices::DeviceKind::Serial) {
|
||||
desiredSerialEndpoints.insert(n.endpoint);
|
||||
if (!m_serialDevicesByEndpoint.contains(n.endpoint)) {
|
||||
initializeSerialDevice(n.endpoint, n.params);
|
||||
}
|
||||
m_deviceEndpointsMap.insert(n.endpoint, kind);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove runtime devices that are no longer desired/offline
|
||||
const auto currentKeys = m_serialDevicesByEndpoint.keys();
|
||||
for (const auto& endpoint : currentKeys) {
|
||||
if (!desiredSerialEndpoints.contains(endpoint)) {
|
||||
shutdownSerialDevice(endpoint);
|
||||
m_deviceEndpointsMap.remove(endpoint);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DeviceBusManager::shutdown()
|
||||
{
|
||||
// 关闭并销毁所有串口设备
|
||||
|
||||
@@ -20,6 +20,7 @@ public:
|
||||
void setRegistry(softbus::device_bus::registry::IDeviceRegistry* registry);
|
||||
|
||||
bool initialize(const QJsonObject& rootConfig);
|
||||
bool reconcileFromTreeModel();
|
||||
void shutdown();
|
||||
|
||||
private:
|
||||
|
||||
@@ -49,6 +49,19 @@ bool DeviceMonitorService::start()
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool DeviceMonitorService::initialScan()
|
||||
{
|
||||
bool any = false;
|
||||
for (auto& m : m_monitors) {
|
||||
const auto snapshot = m->snapshotExisting();
|
||||
for (const auto& ev : snapshot) {
|
||||
onEvent(ev);
|
||||
any = true;
|
||||
}
|
||||
}
|
||||
return any;
|
||||
}
|
||||
|
||||
void DeviceMonitorService::stop()
|
||||
{
|
||||
if (!m_started) return;
|
||||
|
||||
@@ -23,6 +23,7 @@ public:
|
||||
|
||||
bool start();
|
||||
void stop();
|
||||
bool initialScan();
|
||||
void setRegistry(softbus::device_bus::registry::IDeviceRegistry* registry);
|
||||
void setDirtyCallback(std::function<void()> cb);
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include <QObject>
|
||||
|
||||
@@ -20,6 +21,7 @@ public:
|
||||
|
||||
virtual bool start() = 0;
|
||||
virtual void stop() = 0;
|
||||
virtual std::vector<DeviceEvent> snapshotExisting() = 0;
|
||||
|
||||
void setCallback(EventCallback cb) { m_cb = std::move(cb); }
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <ifaddrs.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <linux/netlink.h>
|
||||
@@ -149,6 +150,38 @@ void NetlinkDeviceMonitor::onReadable()
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<DeviceEvent> NetlinkDeviceMonitor::snapshotExisting()
|
||||
{
|
||||
std::vector<DeviceEvent> out;
|
||||
|
||||
ifaddrs* ifaddr = nullptr;
|
||||
if (::getifaddrs(&ifaddr) != 0) {
|
||||
return out;
|
||||
}
|
||||
|
||||
QSet<QString> seen;
|
||||
for (ifaddrs* it = ifaddr; it != nullptr; it = it->ifa_next) {
|
||||
if (!it->ifa_name) continue;
|
||||
|
||||
const QString ifname = QString::fromUtf8(it->ifa_name);
|
||||
if (ifname.isEmpty() || seen.contains(ifname)) continue;
|
||||
seen.insert(ifname);
|
||||
|
||||
DeviceEvent ev;
|
||||
ev.timestamp = QDateTime::currentDateTimeUtc();
|
||||
ev.type = DeviceEventType::NetInterface;
|
||||
ev.action = DeviceEventAction::Add;
|
||||
ev.subsystem = QStringLiteral("net");
|
||||
ev.ifname = ifname;
|
||||
ev.ifindex = ::if_nametoindex(it->ifa_name);
|
||||
ev.linkUp = (it->ifa_flags & IFF_RUNNING) != 0;
|
||||
out.push_back(ev);
|
||||
}
|
||||
|
||||
::freeifaddrs(ifaddr);
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace softbus::device_bus::monitor
|
||||
|
||||
#endif // __linux__
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <QSocketNotifier>
|
||||
#include <QSet>
|
||||
#include <QHash>
|
||||
#include <vector>
|
||||
|
||||
namespace softbus::device_bus::monitor
|
||||
{
|
||||
@@ -20,6 +21,7 @@ public:
|
||||
|
||||
bool start() override;
|
||||
void stop() override;
|
||||
std::vector<DeviceEvent> snapshotExisting() override;
|
||||
|
||||
private:
|
||||
void onReadable();
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#if defined(__linux__)
|
||||
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
#include <QRegularExpression>
|
||||
|
||||
#include <QFileInfo>
|
||||
|
||||
@@ -10,6 +12,37 @@
|
||||
|
||||
namespace softbus::device_bus::monitor
|
||||
{
|
||||
namespace {
|
||||
|
||||
bool isWhitelistedSerialDevnode(const QString& devnode)
|
||||
{
|
||||
if (devnode.startsWith(QStringLiteral("/dev/ttyUSB"))) return true;
|
||||
if (devnode.startsWith(QStringLiteral("/dev/ttyACM"))) return true;
|
||||
|
||||
// Board UART allowlist: ttyS1 ~ ttyS6
|
||||
static const QRegularExpression kTtySRe(QStringLiteral("^/dev/ttyS(\\d+)$"));
|
||||
const auto m = kTtySRe.match(devnode);
|
||||
if (m.hasMatch()) {
|
||||
bool ok = false;
|
||||
const int n = m.captured(1).toInt(&ok);
|
||||
if (ok && n >= 1 && n <= 6) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isConsoleLikeTty(const QString& devnode)
|
||||
{
|
||||
if (devnode == QStringLiteral("/dev/console")) return true;
|
||||
if (devnode == QStringLiteral("/dev/tty")) return true;
|
||||
if (devnode == QStringLiteral("/dev/ptmx")) return true;
|
||||
if (devnode.startsWith(QStringLiteral("/dev/pts/"))) return true;
|
||||
|
||||
// Linux VT consoles: /dev/tty0.../dev/tty63 (without 'S')
|
||||
static const QRegularExpression kVtRe(QStringLiteral("^/dev/tty(\\d+)$"));
|
||||
return kVtRe.match(devnode).hasMatch();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
UdevDeviceMonitor::UdevDeviceMonitor(QObject* parent)
|
||||
: IDeviceMonitor(parent)
|
||||
@@ -94,35 +127,7 @@ void UdevDeviceMonitor::onReadable()
|
||||
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) {
|
||||
if (!shouldKeepEvent(ev)) {
|
||||
udev_device_unref(dev);
|
||||
continue;
|
||||
}
|
||||
@@ -132,6 +137,117 @@ void UdevDeviceMonitor::onReadable()
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<DeviceEvent> UdevDeviceMonitor::snapshotExisting()
|
||||
{
|
||||
std::vector<DeviceEvent> out;
|
||||
if (!m_udev) {
|
||||
// Snapshot can be requested before start(); create temp context.
|
||||
m_udev = udev_new();
|
||||
if (!m_udev) {
|
||||
LOG_ERROR() << "UdevDeviceMonitor: snapshot udev_new failed";
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
udev_enumerate* en = udev_enumerate_new(m_udev);
|
||||
if (!en) {
|
||||
return out;
|
||||
}
|
||||
|
||||
udev_enumerate_add_match_subsystem(en, "tty");
|
||||
udev_enumerate_add_match_subsystem(en, "usb");
|
||||
udev_enumerate_add_match_subsystem(en, "block");
|
||||
if (udev_enumerate_scan_devices(en) != 0) {
|
||||
udev_enumerate_unref(en);
|
||||
return out;
|
||||
}
|
||||
|
||||
udev_list_entry* devices = udev_enumerate_get_list_entry(en);
|
||||
udev_list_entry* entry = nullptr;
|
||||
udev_list_entry_foreach(entry, devices) {
|
||||
const char* path = udev_list_entry_get_name(entry);
|
||||
if (!path) continue;
|
||||
|
||||
udev_device* dev = udev_device_new_from_syspath(m_udev, path);
|
||||
if (!dev) continue;
|
||||
|
||||
DeviceEvent ev = toEvent(dev, "add");
|
||||
if (shouldKeepEvent(ev)) {
|
||||
out.push_back(ev);
|
||||
}
|
||||
|
||||
udev_device_unref(dev);
|
||||
}
|
||||
|
||||
udev_enumerate_unref(en);
|
||||
return out;
|
||||
}
|
||||
|
||||
bool UdevDeviceMonitor::shouldKeepEvent(const DeviceEvent& ev) const
|
||||
{
|
||||
// Drop unknown action (usually noise / incomplete events)
|
||||
if (ev.action == DeviceEventAction::Unknown) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Serial tty: keep only business serial nodes.
|
||||
if (ev.subsystem == QStringLiteral("tty")) {
|
||||
if (ev.devnode.isEmpty()) {
|
||||
LOG_ERROR() << "UdevDeviceMonitor: devnode is empty" << ev.devnode;
|
||||
return false;
|
||||
}
|
||||
if (isConsoleLikeTty(ev.devnode)) {
|
||||
LOG_INFO() << "UdevDeviceMonitor: console like tty" << ev.devnode;
|
||||
return false;
|
||||
}
|
||||
if (!isWhitelistedSerialDevnode(ev.devnode)) {
|
||||
LOG_INFO() << "UdevDeviceMonitor: not whitelisted serial devnode" << ev.devnode;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 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")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Drop most "change" spam for usb (keep add/remove)
|
||||
if (ev.action == DeviceEventAction::Change) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If it carries no stable identifiers, it's not useful
|
||||
if (ev.devnode.isEmpty() && ev.vid.isEmpty() && ev.pid.isEmpty() && ev.serial.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Drop Linux root hubs/controller pseudo devices.
|
||||
if (ev.vid == QStringLiteral("1d6b")) {
|
||||
return false;
|
||||
}
|
||||
const QString modelLower = ev.model.toLower();
|
||||
if (modelLower.contains(QStringLiteral("hub")) ||
|
||||
modelLower.contains(QStringLiteral("host_controller")) ||
|
||||
modelLower.contains(QStringLiteral("xhci"))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Block: keep only usb-backed storage add/remove, drop non-storage blocks.
|
||||
if (ev.subsystem == QStringLiteral("block")) {
|
||||
if (ev.type != DeviceEventType::Storage) {
|
||||
return false;
|
||||
}
|
||||
if (ev.action == DeviceEventAction::Change) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QString UdevDeviceMonitor::getProp(struct udev_device* dev, const char* key)
|
||||
{
|
||||
if (!dev || !key) return {};
|
||||
|
||||
@@ -20,9 +20,11 @@ public:
|
||||
|
||||
bool start() override;
|
||||
void stop() override;
|
||||
std::vector<DeviceEvent> snapshotExisting() override;
|
||||
|
||||
private:
|
||||
void onReadable();
|
||||
bool shouldKeepEvent(const DeviceEvent& ev) const;
|
||||
DeviceEvent toEvent(struct udev_device* dev, const char* action);
|
||||
static QString getProp(struct udev_device* dev, const char* key);
|
||||
|
||||
|
||||
@@ -38,5 +38,11 @@ void WindowsDeviceMonitor::stop()
|
||||
#endif
|
||||
}
|
||||
|
||||
std::vector<DeviceEvent> WindowsDeviceMonitor::snapshotExisting()
|
||||
{
|
||||
// TODO: implement full snapshot on Windows.
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace softbus::device_bus::monitor
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ public:
|
||||
|
||||
bool start() override;
|
||||
void stop() override;
|
||||
std::vector<DeviceEvent> snapshotExisting() override;
|
||||
|
||||
private:
|
||||
#if defined(_WIN32)
|
||||
|
||||
@@ -85,6 +85,27 @@ QString DeviceRegistry::buildStableKey(const softbus::device_bus::monitor::Devic
|
||||
{
|
||||
const QString type = inferType(ev);
|
||||
|
||||
// Important: ttyACM/ttyUSB 可能“同一条物理设备多路口”但 serial 相同,
|
||||
// 旧逻辑会导致 stableKey 冲突并相互覆盖 endpoint。
|
||||
// tty 事件:devnode 必须用于唯一标识(ttyACM 可能多路口但 serial 可能相同)。
|
||||
// 若具备 SYSPATH,也一并拼接,便于跨层级归并与排查。
|
||||
if (ev.type == softbus::device_bus::monitor::DeviceEventType::Serial ||
|
||||
ev.subsystem == QStringLiteral("tty")) {
|
||||
const QString syspath = ev.props.value(QStringLiteral("SYSPATH"));
|
||||
if (!ev.devnode.isEmpty()) {
|
||||
if (!syspath.isEmpty()) {
|
||||
return type + QStringLiteral("|syspath|") + syspath + QStringLiteral("|devnode|") + ev.devnode;
|
||||
}
|
||||
return type + QStringLiteral("|devnode|") + ev.devnode;
|
||||
}
|
||||
if (!syspath.isEmpty()) {
|
||||
return type + QStringLiteral("|syspath|") + syspath;
|
||||
}
|
||||
if (!ev.serial.isEmpty()) {
|
||||
return type + QStringLiteral("|serial|") + ev.serial;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ev.serial.isEmpty()) {
|
||||
return type + QStringLiteral("|serial|") + ev.serial;
|
||||
}
|
||||
@@ -109,6 +130,15 @@ QString DeviceRegistry::buildStableKey(const softbus::device_bus::monitor::Devic
|
||||
|
||||
QString DeviceRegistry::buildPreferredNodeId(const softbus::device_bus::monitor::DeviceEvent& ev, const QString& stableKey)
|
||||
{
|
||||
// For tty devices (ttyS/ttyUSB/ttyACM...), a single physical device may expose
|
||||
// multiple ports that share the same serial. If we only use `serial` as id,
|
||||
// upsert() will collide by preferredId and overwrite endpoints.
|
||||
if (ev.type == softbus::device_bus::monitor::DeviceEventType::Serial ||
|
||||
ev.subsystem == QStringLiteral("tty")) {
|
||||
const QByteArray hash = QCryptographicHash::hash(stableKey.toUtf8(), QCryptographicHash::Sha1).toHex();
|
||||
return QStringLiteral("dev_") + QString::fromUtf8(hash.left(12));
|
||||
}
|
||||
|
||||
if (!ev.serial.isEmpty()) {
|
||||
return QStringLiteral("dev_serial_") + ev.serial;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user