prevent auto devices from being manually removed, allow plugins to remove them again

This commit is contained in:
Michael Zanetti 2017-09-07 09:38:53 +02:00
parent 6a9759eb62
commit 3b42d16777
11 changed files with 144 additions and 25 deletions

View File

@ -146,10 +146,15 @@ QPair<DeviceManager::DeviceError, QList<RuleId> > GuhCore::removeConfiguredDevic
return QPair<DeviceManager::DeviceError, QList<RuleId> > (DeviceManager::DeviceErrorDeviceIsChild, QList<RuleId>());
}
if (device->autoCreated()) {
qCWarning(dcDeviceManager) << "This device has been auto-created and cannot be deleted manually.";
return QPair<DeviceManager::DeviceError, QList<RuleId> >(DeviceManager::DeviceErrorCreationMethodNotSupported, {});
}
// Check if this device has child devices
QList<Device *> devicesToRemove;
devicesToRemove.append(device);
QList<Device *> childDevices = m_deviceManager->findChildDevices(device);
QList<Device *> childDevices = m_deviceManager->findChildDevices(deviceId);
if (!childDevices.isEmpty()) {
foreach (Device *child, childDevices) {
devicesToRemove.append(child);
@ -236,10 +241,15 @@ DeviceManager::DeviceError GuhCore::removeConfiguredDevice(const DeviceId &devic
return DeviceManager::DeviceErrorDeviceIsChild;
}
if (device->autoCreated()) {
qCWarning(dcDeviceManager) << "This device has been auto-created and cannot be deleted manually.";
return DeviceManager::DeviceErrorCreationMethodNotSupported;
}
// Check if this device has child devices
QList<Device *> devicesToRemove;
devicesToRemove.append(device);
QList<Device *> childDevices = m_deviceManager->findChildDevices(device);
QList<Device *> childDevices = m_deviceManager->findChildDevices(deviceId);
if (!childDevices.isEmpty()) {
foreach (Device *child, childDevices) {
devicesToRemove.append(child);
@ -437,6 +447,7 @@ void GuhCore::init() {
connect(m_deviceManager, &DeviceManager::deviceAdded, this, &GuhCore::deviceAdded);
connect(m_deviceManager, &DeviceManager::deviceChanged, this, &GuhCore::deviceChanged);
connect(m_deviceManager, &DeviceManager::deviceRemoved, this, &GuhCore::deviceRemoved);
connect(m_deviceManager, &DeviceManager::deviceDisappeared, this, &GuhCore::onDeviceDisappeared);
connect(m_deviceManager, &DeviceManager::actionExecutionFinished, this, &GuhCore::actionExecutionFinished);
connect(m_deviceManager, &DeviceManager::devicesDiscovered, this, &GuhCore::devicesDiscovered);
connect(m_deviceManager, &DeviceManager::deviceSetupFinished, this, &GuhCore::deviceSetupFinished);
@ -566,4 +577,58 @@ void GuhCore::actionExecutionFinished(const ActionId &id, DeviceManager::DeviceE
m_logger->logAction(action, status == DeviceManager::DeviceErrorNoError ? Logging::LoggingLevelInfo : Logging::LoggingLevelAlert, status);
}
void GuhCore::onDeviceDisappeared(const DeviceId &deviceId)
{
Device *device = m_deviceManager->findConfiguredDevice(deviceId);
if (!device) {
return;
}
// Check if this device has child devices
QList<Device *> devicesToRemove;
devicesToRemove.append(device);
QList<Device *> childDevices = m_deviceManager->findChildDevices(deviceId);
if (!childDevices.isEmpty()) {
foreach (Device *child, childDevices) {
devicesToRemove.append(child);
}
}
// check devices
QList<RuleId> offendingRules;
qCDebug(dcDeviceManager) << "Devices to remove:";
foreach (Device *d, devicesToRemove) {
qCDebug(dcDeviceManager) << " -> " << d->name() << d->id().toString();
// Check if device is in a rule
foreach (const RuleId &ruleId, m_ruleEngine->findRules(d->id())) {
qCDebug(dcDeviceManager) << " -> in rule:" << ruleId.toString();
if (!offendingRules.contains(ruleId)) {
offendingRules.append(ruleId);
}
}
}
// update involved rules
foreach (const RuleId &ruleId, offendingRules) {
foreach (Device *d, devicesToRemove) {
m_ruleEngine->removeDeviceFromRule(ruleId, d->id());
}
}
// remove the child devices
foreach (Device *d, childDevices) {
DeviceManager::DeviceError removeError = m_deviceManager->removeConfiguredDevice(d->id());
if (removeError == DeviceManager::DeviceErrorNoError) {
m_logger->removeDeviceLogs(d->id());
}
}
// delete the device
DeviceManager::DeviceError removeError = m_deviceManager->removeConfiguredDevice(deviceId);
if (removeError == DeviceManager::DeviceErrorNoError) {
m_logger->removeDeviceLogs(deviceId);
}
}
}

View File

@ -122,6 +122,7 @@ private slots:
void onDateTimeChanged(const QDateTime &dateTime);
void onLocaleChanged();
void actionExecutionFinished(const ActionId &id, DeviceManager::DeviceError status);
void onDeviceDisappeared(const DeviceId &deviceId);
};

View File

@ -741,7 +741,7 @@ DeviceManager::DeviceError DeviceManager::addConfiguredDeviceInternal(const Devi
break;
}
m_configuredDevices.append(device);
m_configuredDevices.insert(device->id(), device);
storeConfiguredDevices();
postSetupDevice(device);
@ -755,12 +755,10 @@ DeviceManager::DeviceError DeviceManager::addConfiguredDeviceInternal(const Devi
* Returns \l{DeviceError} to inform about the result. */
DeviceManager::DeviceError DeviceManager::removeConfiguredDevice(const DeviceId &deviceId)
{
Device *device = findConfiguredDevice(deviceId);
Device *device = m_configuredDevices.take(deviceId);
if (!device) {
return DeviceErrorDeviceNotFound;
}
m_configuredDevices.removeAll(device);
m_devicePlugins.value(device->pluginId())->deviceRemoved(device);
// check if this plugin still needs the guhTimer call
@ -806,7 +804,7 @@ Device *DeviceManager::findConfiguredDevice(const DeviceId &id) const
/*! Returns all configured \{Device}{Devices} in the system. */
QList<Device *> DeviceManager::configuredDevices() const
{
return m_configuredDevices;
return m_configuredDevices.values();
}
/*! Returns all \l{Device}{Devices} matching the \l{DeviceClass} referred by \a deviceClassId. */
@ -822,11 +820,11 @@ QList<Device *> DeviceManager::findConfiguredDevices(const DeviceClassId &device
}
/*! Returns all child \l{Device}{Devices} of the given \a device. */
QList<Device *> DeviceManager::findChildDevices(Device *device) const
QList<Device *> DeviceManager::findChildDevices(const DeviceId &id) const
{
QList<Device *> ret;
foreach (Device *d, m_configuredDevices) {
if (d->parentId() == device->id()) {
if (d->parentId() == id) {
ret.append(d);
}
}
@ -1101,7 +1099,8 @@ void DeviceManager::loadPlugins()
connect(pluginIface, &DevicePlugin::deviceSetupFinished, this, &DeviceManager::slotDeviceSetupFinished);
connect(pluginIface, &DevicePlugin::actionExecutionFinished, this, &DeviceManager::actionExecutionFinished);
connect(pluginIface, &DevicePlugin::pairingFinished, this, &DeviceManager::slotPairingFinished);
connect(pluginIface, &DevicePlugin::autoDevicesAppeared, this, &DeviceManager::autoDevicesAppeared);
connect(pluginIface, &DevicePlugin::autoDevicesAppeared, this, &DeviceManager::onAutoDevicesAppeared);
connect(pluginIface, &DevicePlugin::autoDeviceDisappeared, this, &DeviceManager::onAutoDeviceDisappeared);
}
}
}
@ -1114,6 +1113,7 @@ void DeviceManager::loadConfiguredDevices()
foreach (const QString &idString, settings.childGroups()) {
settings.beginGroup(idString);
Device *device = new Device(PluginId(settings.value("pluginid").toString()), DeviceId(idString), DeviceClassId(settings.value("deviceClassId").toString()), this);
device->m_autoCreated = settings.value("autoCreated").toBool();
device->setName(settings.value("devicename").toString());
device->setParentId(DeviceId(settings.value("parentid", QUuid()).toString()));
@ -1130,7 +1130,7 @@ void DeviceManager::loadConfiguredDevices()
// it means that it was working at some point so lets still add it as there might
// be rules associated with this device. Device::setupCompleted() will be false.
DeviceSetupStatus status = setupDevice(device);
m_configuredDevices.append(device);
m_configuredDevices.insert(device->id(), device);
if (status == DeviceSetupStatus::DeviceSetupStatusSuccess)
postSetupDevice(device);
@ -1144,6 +1144,7 @@ void DeviceManager::storeConfiguredDevices()
settings.beginGroup("DeviceConfig");
foreach (Device *device, m_configuredDevices) {
settings.beginGroup(device->id().toString());
settings.setValue("autoCreated", device->autoCreated());
settings.setValue("devicename", device->name());
settings.setValue("deviceClassId", device->deviceClassId().toString());
settings.setValue("pluginid", device->pluginId().toString());
@ -1202,7 +1203,7 @@ void DeviceManager::slotDeviceSetupFinished(Device *device, DeviceManager::Devic
}
if (status == DeviceSetupStatusFailure) {
if (m_configuredDevices.contains(device)) {
if (m_configuredDevices.contains(device->id())) {
if (m_asyncDeviceReconfiguration.contains(device)) {
m_asyncDeviceReconfiguration.removeAll(device);
qCWarning(dcDeviceManager) << QString("Error in device setup after reconfiguration. Device %1 (%2) will not be functional.").arg(device->name()).arg(device->id().toString());
@ -1226,8 +1227,8 @@ void DeviceManager::slotDeviceSetupFinished(Device *device, DeviceManager::Devic
// A device might be in here already if loaded from storedDevices. If it's not in the configuredDevices,
// lets add it now.
if (!m_configuredDevices.contains(device)) {
m_configuredDevices.append(device);
if (!m_configuredDevices.contains(device->id())) {
m_configuredDevices.insert(device->id(), device);
emit deviceAdded(device);
storeConfiguredDevices();
}
@ -1335,14 +1336,14 @@ void DeviceManager::slotPairingFinished(const PairingTransactionId &pairingTrans
break;
}
m_configuredDevices.append(device);
m_configuredDevices.insert(device->id(), device);
emit deviceAdded(device);
storeConfiguredDevices();
emit deviceSetupFinished(device, DeviceError::DeviceErrorNoError);
postSetupDevice(device);
}
void DeviceManager::autoDevicesAppeared(const DeviceClassId &deviceClassId, const QList<DeviceDescriptor> &deviceDescriptors)
void DeviceManager::onAutoDevicesAppeared(const DeviceClassId &deviceClassId, const QList<DeviceDescriptor> &deviceDescriptors)
{
DeviceClass deviceClass = findDeviceClass(deviceClassId);
if (!deviceClass.isValid()) {
@ -1356,6 +1357,7 @@ void DeviceManager::autoDevicesAppeared(const DeviceClassId &deviceClassId, cons
foreach (const DeviceDescriptor &deviceDescriptor, deviceDescriptors) {
Device *device = new Device(plugin->pluginId(), deviceClassId, this);
device->m_autoCreated = true;
device->setName(deviceClass.name());
device->setParams(deviceDescriptor.params());
@ -1370,7 +1372,7 @@ void DeviceManager::autoDevicesAppeared(const DeviceClassId &deviceClassId, cons
break;
case DeviceSetupStatusSuccess:
qCDebug(dcDeviceManager) << "Device setup complete.";
m_configuredDevices.append(device);
m_configuredDevices.insert(device->id(), device);
storeConfiguredDevices();
emit deviceSetupFinished(device, DeviceError::DeviceErrorNoError);
emit deviceAdded(device);
@ -1380,6 +1382,31 @@ void DeviceManager::autoDevicesAppeared(const DeviceClassId &deviceClassId, cons
}
}
void DeviceManager::onAutoDeviceDisappeared(const DeviceId &deviceId)
{
DevicePlugin *plugin = static_cast<DevicePlugin*>(sender());
Device *device = m_configuredDevices.value(deviceId);
if (!device) {
qWarning(dcDeviceManager) << "Received an autoDeviceDisappeared signal but don't know this device:" << deviceId;
return;
}
DeviceClass deviceClass = m_supportedDevices.value(device->deviceClassId());
if (deviceClass.pluginId() != plugin->pluginId()) {
qWarning(dcDeviceManager) << "Received a autoDeviceDisappeared signal but emitting plugin does not own the device";
return;
}
if (!device->autoCreated()) {
qWarning(dcDeviceManager) << "Received an autoDeviceDisappeared signal but device creationMethod is not CreateMothodAuto";
return;
}
emit deviceDisappeared(deviceId);
}
void DeviceManager::slotDeviceStateValueChanged(const QUuid &stateTypeId, const QVariant &value)
{
Device *device = qobject_cast<Device*>(sender());

View File

@ -138,7 +138,7 @@ public:
Device* findConfiguredDevice(const DeviceId &id) const;
QList<Device *> findConfiguredDevices(const DeviceClassId &deviceClassId) const;
QList<Device *> findChildDevices(Device *device) const;
QList<Device *> findChildDevices(const DeviceId &id) const;
DeviceClass findDeviceClass(const DeviceClassId &deviceClassId) const;
DeviceError verifyParams(const QList<ParamType> paramTypes, ParamList &params, bool requireAll = true);
@ -152,6 +152,7 @@ signals:
void eventTriggered(const Event &event);
void deviceStateChanged(Device *device, const QUuid &stateTypeId, const QVariant &value);
void deviceRemoved(const DeviceId &deviceId);
void deviceDisappeared(const DeviceId &deviceId);
void deviceAdded(Device *device);
void deviceChanged(Device *device);
void devicesDiscovered(const DeviceClassId &deviceClassId, const QList<DeviceDescriptor> &devices);
@ -172,7 +173,8 @@ private slots:
void slotDevicesDiscovered(const DeviceClassId &deviceClassId, const QList<DeviceDescriptor> deviceDescriptors);
void slotDeviceSetupFinished(Device *device, DeviceManager::DeviceSetupStatus status);
void slotPairingFinished(const PairingTransactionId &pairingTransactionId, DeviceManager::DeviceSetupStatus status);
void autoDevicesAppeared(const DeviceClassId &deviceClassId, const QList<DeviceDescriptor> &deviceDescriptors);
void onAutoDevicesAppeared(const DeviceClassId &deviceClassId, const QList<DeviceDescriptor> &deviceDescriptors);
void onAutoDeviceDisappeared(const DeviceId &deviceId);
// Only connect this to Devices. It will query the sender()
void slotDeviceStateValueChanged(const QUuid &stateTypeId, const QVariant &value);
@ -200,7 +202,7 @@ private:
QHash<VendorId, Vendor> m_supportedVendors;
QHash<VendorId, QList<DeviceClassId> > m_vendorDeviceMap;
QHash<DeviceClassId, DeviceClass> m_supportedDevices;
QList<Device *> m_configuredDevices;
QHash<DeviceId, Device*> m_configuredDevices;
QHash<DeviceDescriptorId, DeviceDescriptor> m_discoveredDevices;
QHash<PluginId, DevicePlugin*> m_devicePlugins;

View File

@ -50,8 +50,7 @@ Device::Device(const PluginId &pluginId, const DeviceId &id, const DeviceClassId
QObject(parent),
m_id(id),
m_deviceClassId(deviceClassId),
m_pluginId(pluginId),
m_setupComplete(false)
m_pluginId(pluginId)
{
}
@ -61,8 +60,7 @@ Device::Device(const PluginId &pluginId, const DeviceClassId &deviceClassId, QOb
QObject(parent),
m_id(DeviceId::createDeviceId()),
m_deviceClassId(deviceClassId),
m_pluginId(pluginId),
m_setupComplete(false)
m_pluginId(pluginId)
{
}
@ -233,6 +231,12 @@ bool Device::setupComplete() const
return m_setupComplete;
}
/*! Returns true if this device has been auto-created (not created by the user) */
bool Device::autoCreated() const
{
return m_autoCreated;
}
void Device::setSetupComplete(const bool &complete)
{
m_setupComplete = complete;

View File

@ -69,6 +69,7 @@ public:
void setParentId(const DeviceId &parentId);
bool setupComplete() const;
bool autoCreated() const;
signals:
void stateValueChanged(const QUuid &stateTypeId, const QVariant &value);
@ -88,7 +89,8 @@ private:
QString m_name;
ParamList m_params;
QList<State> m_states;
bool m_setupComplete;
bool m_setupComplete = false;
bool m_autoCreated = false;
};
#endif

View File

@ -120,6 +120,14 @@
will be in \a deviceDescriptors. This signal can only emitted from devices with the \l{DeviceClass}{CreateMethodAuto}.
*/
/*!
\fn void DevicePlugin::autoDeviceDisappeared(const DeviceId &id)
Emit this signal when a device which was created by \l{DevicePlugin::autoDevicesAppeared} has been removed from the system.
Be careful with this, as this will completely remove the device from the system and with it all the associated rules. Only
emit this if you are sure that a device will never come back. This signal should not be emitted for child auto devices
when the parent who created them is removed. The system will automatically remove all child devices in such a case.
*/
/*!
\fn void DevicePlugin::emitEvent(const Event &event)
To produce a new event in the system, create a new \l{Event} and emit it with \a event.

View File

@ -108,6 +108,7 @@ signals:
void actionExecutionFinished(const ActionId &id, DeviceManager::DeviceError status);
void configValueChanged(const ParamTypeId &paramTypeId, const QVariant &value);
void autoDevicesAppeared(const DeviceClassId &deviceClassId, const QList<DeviceDescriptor> &deviceDescriptors);
void autoDeviceDisappeared(const DeviceId &deviceId);
protected:
DeviceManager *deviceManager() const;

View File

@ -1175,6 +1175,7 @@ void TestDevices::removeDevice_data()
QTest::newRow("Existing Device") << m_mockDeviceId << DeviceManager::DeviceErrorNoError;
QTest::newRow("Not existing Device") << DeviceId::createDeviceId() << DeviceManager::DeviceErrorDeviceNotFound;
QTest::newRow("Auto device") << m_mockDeviceAutoId << DeviceManager::DeviceErrorCreationMethodNotSupported;
}
void TestDevices::removeDevice()

View File

@ -203,6 +203,13 @@ void GuhTestBase::initTestCase()
m_mockDeviceId = DeviceId(response.toMap().value("params").toMap().value("deviceId").toString());
QVERIFY2(!m_mockDeviceId.isNull(), "Newly created mock device must not be null.");
response = injectAndWait("Devices.GetConfiguredDevices", {});
foreach (const QVariant &device, response.toMap().value("params").toMap().value("devices").toList()) {
if (device.toMap().value("deviceClassId").toUuid() == mockDeviceAutoClassId) {
m_mockDeviceAutoId = DeviceId(device.toMap().value("id").toString());
}
}
}
void GuhTestBase::cleanupTestCase()

View File

@ -188,6 +188,7 @@ protected:
int m_mockDevice2Port;
DeviceId m_mockDeviceId;
DeviceId m_mockDeviceAutoId;
QByteArray m_apiToken;
};