File indexing completed on 2024-05-12 05:37:20

0001 /*
0002     SPDX-FileCopyrightText: 2007 Christopher Blauvelt <cblauvelt@gmail.com>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-only
0005 */
0006 
0007 #include "soliddeviceengine.h"
0008 #include "soliddeviceservice.h"
0009 
0010 #include <KLazyLocalizedString>
0011 #include <QDateTime>
0012 #include <QMetaEnum>
0013 #include <Solid/GenericInterface>
0014 #include <klocalizedstring.h>
0015 
0016 #include <KFormat>
0017 #include <KNotification>
0018 #include <QDebug>
0019 
0020 #include <Plasma5Support/DataContainer>
0021 
0022 // TODO: implement in libsolid2
0023 namespace
0024 {
0025 template<class DevIface>
0026 DevIface *getAncestorAs(const Solid::Device &device)
0027 {
0028     for (Solid::Device parent = device.parent(); parent.isValid(); parent = parent.parent()) {
0029         if (parent.is<DevIface>()) {
0030             return parent.as<DevIface>();
0031         }
0032     }
0033     return nullptr;
0034 }
0035 }
0036 
0037 SolidDeviceEngine::SolidDeviceEngine(QObject *parent)
0038     : Plasma5Support::DataEngine(parent)
0039     , m_temperature(nullptr)
0040     , m_notifier(nullptr)
0041 {
0042     m_signalmanager = new DeviceSignalMapManager(this);
0043 
0044     listenForNewDevices();
0045     setMinimumPollingInterval(1000);
0046     connect(this, &Plasma5Support::DataEngine::sourceRemoved, this, &SolidDeviceEngine::sourceWasRemoved);
0047 }
0048 
0049 SolidDeviceEngine::~SolidDeviceEngine()
0050 {
0051 }
0052 
0053 Plasma5Support::Service *SolidDeviceEngine::serviceForSource(const QString &source)
0054 {
0055     return new SolidDeviceService(this, source);
0056 }
0057 
0058 void SolidDeviceEngine::listenForNewDevices()
0059 {
0060     if (m_notifier) {
0061         return;
0062     }
0063 
0064     // detect when new devices are added
0065     m_notifier = Solid::DeviceNotifier::instance();
0066     connect(m_notifier, &Solid::DeviceNotifier::deviceAdded, this, &SolidDeviceEngine::deviceAdded);
0067     connect(m_notifier, &Solid::DeviceNotifier::deviceRemoved, this, &SolidDeviceEngine::deviceRemoved);
0068 }
0069 
0070 bool SolidDeviceEngine::sourceRequestEvent(const QString &name)
0071 {
0072     if (name.startsWith('/')) {
0073         Solid::Device device = Solid::Device(name);
0074         if (device.isValid()) {
0075             if (m_devicemap.contains(name)) {
0076                 return true;
0077             } else {
0078                 m_devicemap[name] = device;
0079                 return populateDeviceData(name);
0080             }
0081         }
0082     } else {
0083         Solid::Predicate predicate = Solid::Predicate::fromString(name);
0084         if (predicate.isValid() && !m_predicatemap.contains(name)) {
0085             foreach (const Solid::Device &device, Solid::Device::listFromQuery(predicate)) {
0086                 m_predicatemap[name] << device.udi();
0087             }
0088 
0089             setData(name, m_predicatemap[name]);
0090             return true;
0091         }
0092     }
0093 
0094     qDebug() << "Source is not a predicate or a device.";
0095     return false;
0096 }
0097 
0098 void SolidDeviceEngine::sourceWasRemoved(const QString &source)
0099 {
0100     m_devicemap.remove(source);
0101     m_predicatemap.remove(source);
0102 }
0103 
0104 bool SolidDeviceEngine::populateDeviceData(const QString &name)
0105 {
0106     Solid::Device device = m_devicemap.value(name);
0107     if (!device.isValid()) {
0108         return false;
0109     }
0110 
0111     QStringList devicetypes;
0112     setData(name, kli18n("Parent UDI").untranslatedText(), device.parentUdi());
0113     setData(name, kli18n("Vendor").untranslatedText(), device.vendor());
0114     setData(name, kli18n("Product").untranslatedText(), device.product());
0115     setData(name, kli18n("Description").untranslatedText(), device.description());
0116     setData(name, kli18n("Icon").untranslatedText(), device.icon());
0117     setData(name, kli18n("Emblems").untranslatedText(), device.emblems());
0118     setData(name, kli18n("State").untranslatedText(), Idle);
0119     setData(name, kli18n("Operation result").untranslatedText(), Working);
0120     setData(name, kli18n("Timestamp").untranslatedText(), QDateTime::currentDateTimeUtc());
0121 
0122     if (device.is<Solid::Processor>()) {
0123         Solid::Processor *processor = device.as<Solid::Processor>();
0124         if (!processor) {
0125             return false;
0126         }
0127 
0128         devicetypes << kli18n("Processor").untranslatedText();
0129         setData(name, kli18n("Number").untranslatedText(), processor->number());
0130         setData(name, kli18n("Max Speed").untranslatedText(), processor->maxSpeed());
0131         setData(name, kli18n("Can Change Frequency").untranslatedText(), processor->canChangeFrequency());
0132     }
0133     if (device.is<Solid::Block>()) {
0134         Solid::Block *block = device.as<Solid::Block>();
0135         if (!block) {
0136             return false;
0137         }
0138 
0139         devicetypes << kli18n("Block").untranslatedText();
0140         setData(name, kli18n("Major").untranslatedText(), block->deviceMajor());
0141         setData(name, kli18n("Minor").untranslatedText(), block->deviceMinor());
0142         setData(name, kli18n("Device").untranslatedText(), block->device());
0143     }
0144     if (device.is<Solid::StorageAccess>()) {
0145         Solid::StorageAccess *storageaccess = device.as<Solid::StorageAccess>();
0146         if (!storageaccess) {
0147             return false;
0148         }
0149 
0150         devicetypes << kli18n("Storage Access").untranslatedText();
0151         setData(name, kli18n("Accessible").untranslatedText(), storageaccess->isAccessible());
0152         setData(name, kli18n("File Path").untranslatedText(), storageaccess->filePath());
0153 
0154         if (storageaccess->isAccessible()) {
0155             updateStorageSpace(name);
0156         }
0157 
0158         m_signalmanager->mapDevice(storageaccess, device.udi());
0159     }
0160 
0161     if (device.is<Solid::StorageDrive>()) {
0162         Solid::StorageDrive *storagedrive = device.as<Solid::StorageDrive>();
0163         if (!storagedrive) {
0164             return false;
0165         }
0166 
0167         devicetypes << kli18n("Storage Drive").untranslatedText();
0168 
0169         QStringList bus;
0170         bus << kli18n("Ide").untranslatedText() << kli18n("Usb").untranslatedText() << kli18n("Ieee1394").untranslatedText()
0171             << kli18n("Scsi").untranslatedText() << kli18n("Sata").untranslatedText() << kli18n("Platform").untranslatedText();
0172         QStringList drivetype;
0173         drivetype << kli18n("Hard Disk").untranslatedText() << kli18n("Cdrom Drive").untranslatedText() << kli18n("Floppy").untranslatedText()
0174                   << kli18n("Tape").untranslatedText() << kli18n("Compact Flash").untranslatedText() << kli18n("Memory Stick").untranslatedText()
0175                   << kli18n("Smart Media").untranslatedText() << kli18n("SdMmc").untranslatedText() << kli18n("Xd").untranslatedText();
0176 
0177         setData(name, kli18n("Bus").untranslatedText(), bus.at((int)storagedrive->bus()));
0178         setData(name, kli18n("Drive Type").untranslatedText(), drivetype.at((int)storagedrive->driveType()));
0179         setData(name, kli18n("Removable").untranslatedText(), storagedrive->isRemovable());
0180         setData(name, kli18n("Hotpluggable").untranslatedText(), storagedrive->isHotpluggable());
0181 
0182         updateHardDiskTemperature(name);
0183     } else {
0184         bool isRemovable = false;
0185         bool isHotpluggable = false;
0186         Solid::StorageDrive *drive = getAncestorAs<Solid::StorageDrive>(device);
0187         if (drive) {
0188             // remove check for isHotpluggable() when plasmoids are changed to check for both properties
0189             isRemovable = (drive->isRemovable() || drive->isHotpluggable());
0190             isHotpluggable = drive->isHotpluggable();
0191         }
0192         setData(name, kli18n("Removable").untranslatedText(), isRemovable);
0193         setData(name, kli18n("Hotpluggable").untranslatedText(), isHotpluggable);
0194     }
0195 
0196     if (device.is<Solid::OpticalDrive>()) {
0197         Solid::OpticalDrive *opticaldrive = device.as<Solid::OpticalDrive>();
0198         if (!opticaldrive) {
0199             return false;
0200         }
0201 
0202         devicetypes << kli18n("Optical Drive").untranslatedText();
0203 
0204         QStringList supportedtypes;
0205         Solid::OpticalDrive::MediumTypes mediatypes = opticaldrive->supportedMedia();
0206         if (mediatypes & Solid::OpticalDrive::Cdr) {
0207             supportedtypes << kli18n("CD-R").untranslatedText();
0208         }
0209         if (mediatypes & Solid::OpticalDrive::Cdrw) {
0210             supportedtypes << kli18n("CD-RW").untranslatedText();
0211         }
0212         if (mediatypes & Solid::OpticalDrive::Dvd) {
0213             supportedtypes << kli18n("DVD").untranslatedText();
0214         }
0215         if (mediatypes & Solid::OpticalDrive::Dvdr) {
0216             supportedtypes << kli18n("DVD-R").untranslatedText();
0217         }
0218         if (mediatypes & Solid::OpticalDrive::Dvdrw) {
0219             supportedtypes << kli18n("DVD-RW").untranslatedText();
0220         }
0221         if (mediatypes & Solid::OpticalDrive::Dvdram) {
0222             supportedtypes << kli18n("DVD-RAM").untranslatedText();
0223         }
0224         if (mediatypes & Solid::OpticalDrive::Dvdplusr) {
0225             supportedtypes << kli18n("DVD+R").untranslatedText();
0226         }
0227         if (mediatypes & Solid::OpticalDrive::Dvdplusrw) {
0228             supportedtypes << kli18n("DVD+RW").untranslatedText();
0229         }
0230         if (mediatypes & Solid::OpticalDrive::Dvdplusdl) {
0231             supportedtypes << kli18n("DVD+DL").untranslatedText();
0232         }
0233         if (mediatypes & Solid::OpticalDrive::Dvdplusdlrw) {
0234             supportedtypes << kli18n("DVD+DLRW").untranslatedText();
0235         }
0236         if (mediatypes & Solid::OpticalDrive::Bd) {
0237             supportedtypes << kli18n("BD").untranslatedText();
0238         }
0239         if (mediatypes & Solid::OpticalDrive::Bdr) {
0240             supportedtypes << kli18n("BD-R").untranslatedText();
0241         }
0242         if (mediatypes & Solid::OpticalDrive::Bdre) {
0243             supportedtypes << kli18n("BD-RE").untranslatedText();
0244         }
0245         if (mediatypes & Solid::OpticalDrive::HdDvd) {
0246             supportedtypes << kli18n("HDDVD").untranslatedText();
0247         }
0248         if (mediatypes & Solid::OpticalDrive::HdDvdr) {
0249             supportedtypes << kli18n("HDDVD-R").untranslatedText();
0250         }
0251         if (mediatypes & Solid::OpticalDrive::HdDvdrw) {
0252             supportedtypes << kli18n("HDDVD-RW").untranslatedText();
0253         }
0254         setData(name, kli18n("Supported Media").untranslatedText(), supportedtypes);
0255 
0256         setData(name, kli18n("Read Speed").untranslatedText(), opticaldrive->readSpeed());
0257         setData(name, kli18n("Write Speed").untranslatedText(), opticaldrive->writeSpeed());
0258 
0259         // the following method return QList<int> so we need to convert it to QList<QVariant>
0260         const QList<int> writespeeds = opticaldrive->writeSpeeds();
0261         QList<QVariant> variantlist;
0262         foreach (int num, writespeeds) {
0263             variantlist << num;
0264         }
0265         setData(name, kli18n("Write Speeds").untranslatedText(), variantlist);
0266     }
0267     if (device.is<Solid::StorageVolume>()) {
0268         Solid::StorageVolume *storagevolume = device.as<Solid::StorageVolume>();
0269         if (!storagevolume) {
0270             return false;
0271         }
0272 
0273         devicetypes << kli18n("Storage Volume").untranslatedText();
0274 
0275         QStringList usagetypes;
0276         usagetypes << i18n("Other") << i18n("Unused") << i18n("File System") << i18n("Partition Table") << i18n("Raid") << i18n("Encrypted");
0277 
0278         if (usagetypes.count() > storagevolume->usage()) {
0279             setData(name, kli18n("Usage").untranslatedText(), usagetypes.at((int)storagevolume->usage()));
0280         } else {
0281             setData(name, kli18n("Usage").untranslatedText(), i18n("Unknown"));
0282         }
0283 
0284         setData(name, kli18n("Ignored").untranslatedText(), storagevolume->isIgnored());
0285         setData(name, kli18n("File System Type").untranslatedText(), storagevolume->fsType());
0286         setData(name, kli18n("Label").untranslatedText(), storagevolume->label());
0287         setData(name, kli18n("UUID").untranslatedText(), storagevolume->uuid());
0288         updateInUse(name);
0289 
0290         // Check if the volume is part of an encrypted container
0291         // This needs to trigger an update for the encrypted container volume since
0292         // libsolid cannot notify us when the accessibility of the container changes
0293         Solid::Device encryptedContainer = storagevolume->encryptedContainer();
0294         if (encryptedContainer.isValid()) {
0295             const QString containerUdi = encryptedContainer.udi();
0296             setData(name, kli18n("Encrypted Container").untranslatedText(), containerUdi);
0297             m_encryptedContainerMap[name] = containerUdi;
0298             // TODO: compress the calls?
0299             forceUpdateAccessibility(containerUdi);
0300         }
0301     }
0302     if (device.is<Solid::OpticalDisc>()) {
0303         Solid::OpticalDisc *opticaldisc = device.as<Solid::OpticalDisc>();
0304         if (!opticaldisc) {
0305             return false;
0306         }
0307 
0308         devicetypes << kli18n("OpticalDisc").untranslatedText();
0309 
0310         // get the content types
0311         QStringList contenttypelist;
0312         const Solid::OpticalDisc::ContentTypes contenttypes = opticaldisc->availableContent();
0313         if (contenttypes.testFlag(Solid::OpticalDisc::Audio)) {
0314             contenttypelist << kli18n("Audio").untranslatedText();
0315         }
0316         if (contenttypes.testFlag(Solid::OpticalDisc::Data)) {
0317             contenttypelist << kli18n("Data").untranslatedText();
0318         }
0319         if (contenttypes.testFlag(Solid::OpticalDisc::VideoCd)) {
0320             contenttypelist << kli18n("Video CD").untranslatedText();
0321         }
0322         if (contenttypes.testFlag(Solid::OpticalDisc::SuperVideoCd)) {
0323             contenttypelist << kli18n("Super Video CD").untranslatedText();
0324         }
0325         if (contenttypes.testFlag(Solid::OpticalDisc::VideoDvd)) {
0326             contenttypelist << kli18n("Video DVD").untranslatedText();
0327         }
0328         if (contenttypes.testFlag(Solid::OpticalDisc::VideoBluRay)) {
0329             contenttypelist << kli18n("Video Blu Ray").untranslatedText();
0330         }
0331         setData(name, kli18n("Available Content").untranslatedText(), contenttypelist);
0332 
0333         QStringList disctypes;
0334         disctypes << kli18n("Unknown Disc Type").untranslatedText() << kli18n("CD Rom").untranslatedText() << kli18n("CD Recordable").untranslatedText()
0335                   << kli18n("CD Rewritable").untranslatedText() << kli18n("DVD Rom").untranslatedText() << kli18n("DVD Ram").untranslatedText()
0336                   << kli18n("DVD Recordable").untranslatedText() << kli18n("DVD Rewritable").untranslatedText()
0337                   << kli18n("DVD Plus Recordable").untranslatedText() << kli18n("DVD Plus Rewritable").untranslatedText()
0338                   << kli18n("DVD Plus Recordable Duallayer").untranslatedText() << kli18n("DVD Plus Rewritable Duallayer").untranslatedText()
0339                   << kli18n("Blu Ray Rom").untranslatedText() << kli18n("Blu Ray Recordable").untranslatedText()
0340                   << kli18n("Blu Ray Rewritable").untranslatedText() << kli18n("HD DVD Rom").untranslatedText()
0341                   << kli18n("HD DVD Recordable").untranslatedText() << kli18n("HD DVD Rewritable").untranslatedText();
0342         //+1 because the enum starts at -1
0343         setData(name, kli18n("Disc Type").untranslatedText(), disctypes.at((int)opticaldisc->discType() + 1));
0344         setData(name, kli18n("Appendable").untranslatedText(), opticaldisc->isAppendable());
0345         setData(name, kli18n("Blank").untranslatedText(), opticaldisc->isBlank());
0346         setData(name, kli18n("Rewritable").untranslatedText(), opticaldisc->isRewritable());
0347         setData(name, kli18n("Capacity").untranslatedText(), opticaldisc->capacity());
0348     }
0349     if (device.is<Solid::Camera>()) {
0350         Solid::Camera *camera = device.as<Solid::Camera>();
0351         if (!camera) {
0352             return false;
0353         }
0354 
0355         devicetypes << kli18n("Camera").untranslatedText();
0356 
0357         setData(name, kli18n("Supported Protocols").untranslatedText(), camera->supportedProtocols());
0358         setData(name, kli18n("Supported Drivers").untranslatedText(), camera->supportedDrivers());
0359         // Cameras are necessarily Removable and Hotpluggable
0360         setData(name, kli18n("Removable").untranslatedText(), true);
0361         setData(name, kli18n("Hotpluggable").untranslatedText(), true);
0362     }
0363     if (device.is<Solid::PortableMediaPlayer>()) {
0364         Solid::PortableMediaPlayer *mediaplayer = device.as<Solid::PortableMediaPlayer>();
0365         if (!mediaplayer) {
0366             return false;
0367         }
0368 
0369         devicetypes << kli18n("Portable Media Player").untranslatedText();
0370 
0371         setData(name, kli18n("Supported Protocols").untranslatedText(), mediaplayer->supportedProtocols());
0372         setData(name, kli18n("Supported Drivers").untranslatedText(), mediaplayer->supportedDrivers());
0373         // Portable Media Players are necessarily Removable and Hotpluggable
0374         setData(name, kli18n("Removable").untranslatedText(), true);
0375         setData(name, kli18n("Hotpluggable").untranslatedText(), true);
0376     }
0377     if (device.is<Solid::Battery>()) {
0378         Solid::Battery *battery = device.as<Solid::Battery>();
0379         if (!battery) {
0380             return false;
0381         }
0382 
0383         devicetypes << kli18n("Battery").untranslatedText();
0384 
0385         QStringList batterytype;
0386         batterytype << kli18n("Unknown Battery").untranslatedText() << kli18n("PDA Battery").untranslatedText() << kli18n("UPS Battery").untranslatedText()
0387                     << kli18n("Primary Battery").untranslatedText() << kli18n("Mouse Battery").untranslatedText()
0388                     << kli18n("Keyboard Battery").untranslatedText() << kli18n("Keyboard Mouse Battery").untranslatedText()
0389                     << kli18n("Camera Battery").untranslatedText() << kli18n("Phone Battery").untranslatedText() << kli18n("Monitor Battery").untranslatedText()
0390                     << kli18n("Gaming Input Battery").untranslatedText() << kli18n("Bluetooth Battery").untranslatedText();
0391 
0392         QStringList chargestate;
0393         chargestate << kli18n("Not Charging").untranslatedText() << kli18n("Charging").untranslatedText() << kli18n("Discharging").untranslatedText()
0394                     << kli18n("Fully Charged").untranslatedText();
0395 
0396         setData(name, kli18n("Plugged In").untranslatedText(), battery->isPresent()); // FIXME Rename when interested parties are adjusted
0397         setData(name, kli18n("Type").untranslatedText(), batterytype.value((int)battery->type()));
0398         setData(name, kli18n("Charge Percent").untranslatedText(), battery->chargePercent());
0399         setData(name, kli18n("Rechargeable").untranslatedText(), battery->isRechargeable());
0400         setData(name, kli18n("Charge State").untranslatedText(), chargestate.at((int)battery->chargeState()));
0401 
0402         m_signalmanager->mapDevice(battery, device.udi());
0403     }
0404 
0405     using namespace Solid;
0406     // we cannot just iterate the enum in reverse order since Battery comes second to last
0407     // and then our phone which also has a battery gets treated as battery :(
0408     static const Solid::DeviceInterface::Type typeOrder[] = {
0409         Solid::DeviceInterface::PortableMediaPlayer,
0410         Solid::DeviceInterface::Camera,
0411         Solid::DeviceInterface::OpticalDisc,
0412         Solid::DeviceInterface::StorageVolume,
0413         Solid::DeviceInterface::OpticalDrive,
0414         Solid::DeviceInterface::StorageDrive,
0415         Solid::DeviceInterface::NetworkShare,
0416         Solid::DeviceInterface::StorageAccess,
0417         Solid::DeviceInterface::Block,
0418         Solid::DeviceInterface::Battery,
0419         Solid::DeviceInterface::Processor,
0420     };
0421 
0422     for (int i = 0; i < 11; ++i) {
0423         const Solid::DeviceInterface *interface = device.asDeviceInterface(typeOrder[i]);
0424         if (interface) {
0425             setData(name, kli18n("Type Description").untranslatedText(), Solid::DeviceInterface::typeDescription(typeOrder[i]));
0426             break;
0427         }
0428     }
0429 
0430     setData(name, kli18n("Device Types").untranslatedText(), devicetypes);
0431     return true;
0432 }
0433 
0434 void SolidDeviceEngine::deviceAdded(const QString &udi)
0435 {
0436     Solid::Device device(udi);
0437 
0438     foreach (const QString &query, m_predicatemap.keys()) {
0439         Solid::Predicate predicate = Solid::Predicate::fromString(query);
0440         if (predicate.matches(device)) {
0441             m_predicatemap[query] << udi;
0442             setData(query, m_predicatemap[query]);
0443         }
0444     }
0445 
0446     if (device.is<Solid::OpticalDisc>()) {
0447         Solid::OpticalDrive *drive = getAncestorAs<Solid::OpticalDrive>(device);
0448         if (drive) {
0449             connect(drive, &Solid::OpticalDrive::ejectRequested, this, &SolidDeviceEngine::setUnmountingState);
0450             connect(drive, &Solid::OpticalDrive::ejectDone, this, &SolidDeviceEngine::setIdleState);
0451         }
0452     } else if (device.is<Solid::StorageVolume>()) {
0453         // update the volume in case of 2-stage devices
0454         if (m_devicemap.contains(udi) && containerForSource(udi)->data().value(kli18n("Size").untranslatedText()).toULongLong() == 0) {
0455             Solid::GenericInterface *iface = device.as<Solid::GenericInterface>();
0456             if (iface) {
0457                 iface->setProperty("udi", udi);
0458                 connect(iface, SIGNAL(propertyChanged(QMap<QString, int>)), this, SLOT(deviceChanged(QMap<QString, int>)));
0459             }
0460         }
0461 
0462         Solid::StorageAccess *access = device.as<Solid::StorageAccess>();
0463         if (access) {
0464             connect(access, &Solid::StorageAccess::setupRequested, this, &SolidDeviceEngine::setMountingState);
0465             connect(access, &Solid::StorageAccess::setupDone, this, &SolidDeviceEngine::setIdleState);
0466             connect(access, &Solid::StorageAccess::teardownRequested, this, &SolidDeviceEngine::setUnmountingState);
0467             connect(access, &Solid::StorageAccess::teardownDone, this, &SolidDeviceEngine::setIdleState);
0468         }
0469     }
0470 }
0471 
0472 void SolidDeviceEngine::setMountingState(const QString &udi)
0473 {
0474     setData(udi, kli18n("State").untranslatedText(), Mounting);
0475     setData(udi, kli18n("Operation result").untranslatedText(), Working);
0476 }
0477 
0478 void SolidDeviceEngine::setUnmountingState(const QString &udi)
0479 {
0480     setData(udi, kli18n("State").untranslatedText(), Unmounting);
0481     setData(udi, kli18n("Operation result").untranslatedText(), Working);
0482 }
0483 
0484 void SolidDeviceEngine::setIdleState(Solid::ErrorType error, QVariant errorData, const QString &udi)
0485 {
0486     Q_UNUSED(errorData)
0487 
0488     if (error == Solid::NoError) {
0489         setData(udi, kli18n("Operation result").untranslatedText(), Successful);
0490     } else {
0491         setData(udi, kli18n("Operation result").untranslatedText(), Unsuccessful);
0492     }
0493     setData(udi, kli18n("State").untranslatedText(), Idle);
0494 
0495     Solid::Device device = m_devicemap.value(udi);
0496     if (!device.isValid()) {
0497         return;
0498     }
0499 
0500     Solid::StorageAccess *storageaccess = device.as<Solid::StorageAccess>();
0501     if (!storageaccess) {
0502         return;
0503     }
0504 
0505     setData(udi, kli18n("Accessible").untranslatedText(), storageaccess->isAccessible());
0506     setData(udi, kli18n("File Path").untranslatedText(), storageaccess->filePath());
0507 }
0508 
0509 void SolidDeviceEngine::deviceChanged(const QMap<QString, int> &props)
0510 {
0511     Solid::GenericInterface *iface = qobject_cast<Solid::GenericInterface *>(sender());
0512     if (iface && iface->isValid() && props.contains(QLatin1String("Size")) && iface->property(QStringLiteral("Size")).toInt() > 0) {
0513         const QString udi = qobject_cast<QObject *>(iface)->property("udi").toString();
0514         if (populateDeviceData(udi))
0515             forceImmediateUpdateOfAllVisualizations();
0516     }
0517 }
0518 
0519 bool SolidDeviceEngine::updateStorageSpace(const QString &udi)
0520 {
0521     Solid::Device device = m_devicemap.value(udi);
0522 
0523     Solid::StorageAccess *storageaccess = device.as<Solid::StorageAccess>();
0524     if (!storageaccess || !storageaccess->isAccessible()) {
0525         return false;
0526     }
0527 
0528     QString path = storageaccess->filePath();
0529     if (!m_paths.contains(path)) {
0530         QTimer *timer = new QTimer(this);
0531         timer->setSingleShot(true);
0532         connect(timer, &QTimer::timeout, [path]() {
0533             KNotification::event(KNotification::Error, i18n("Filesystem is not responding"), i18n("Filesystem mounted at '%1' is not responding", path));
0534         });
0535 
0536         m_paths.insert(path);
0537 
0538         // create job
0539         KIO::FileSystemFreeSpaceJob *job = KIO::fileSystemFreeSpace(QUrl::fromLocalFile(path));
0540 
0541         // delete later timer
0542         connect(job, &KJob::result, timer, &QTimer::deleteLater);
0543 
0544         // collect and process info
0545         connect(job, &KJob::result, this, [this, timer, path, udi, job]() {
0546             timer->stop();
0547 
0548             if (!job->error()) {
0549                 KIO::filesize_t size = job->size();
0550                 KIO::filesize_t available = job->availableSize();
0551                 setData(udi, kli18n("Free Space").untranslatedText(), QVariant(available).toDouble());
0552                 setData(udi, kli18n("Free Space Text").untranslatedText(), KFormat().formatByteSize(available));
0553                 setData(udi, kli18n("Size").untranslatedText(), QVariant(size).toDouble());
0554                 setData(udi, kli18n("Size Text").untranslatedText(), KFormat().formatByteSize(size));
0555             }
0556 
0557             m_paths.remove(path);
0558         });
0559 
0560         // start timer: after 15 seconds we will get an error
0561         timer->start(15000);
0562     }
0563 
0564     return false;
0565 }
0566 
0567 bool SolidDeviceEngine::updateHardDiskTemperature(const QString &udi)
0568 {
0569     Solid::Device device = m_devicemap.value(udi);
0570     Solid::Block *block = device.as<Solid::Block>();
0571     if (!block) {
0572         return false;
0573     }
0574 
0575     if (!m_temperature) {
0576         m_temperature = new HddTemp(this);
0577     }
0578 
0579     if (m_temperature->sources().contains(block->device())) {
0580         setData(udi, kli18n("Temperature").untranslatedText(), m_temperature->data(block->device(), HddTemp::Temperature));
0581         setData(udi, kli18n("Temperature Unit").untranslatedText(), m_temperature->data(block->device(), HddTemp::Unit));
0582         return true;
0583     }
0584 
0585     return false;
0586 }
0587 
0588 bool SolidDeviceEngine::updateEmblems(const QString &udi)
0589 {
0590     Solid::Device device = m_devicemap.value(udi);
0591 
0592     setData(udi, kli18n("Emblems").untranslatedText(), device.emblems());
0593     return true;
0594 }
0595 
0596 bool SolidDeviceEngine::forceUpdateAccessibility(const QString &udi)
0597 {
0598     Solid::Device device = m_devicemap.value(udi);
0599     if (!device.isValid()) {
0600         return false;
0601     }
0602 
0603     updateEmblems(udi);
0604     Solid::StorageAccess *storageaccess = device.as<Solid::StorageAccess>();
0605     if (storageaccess) {
0606         setData(udi, kli18n("Accessible").untranslatedText(), storageaccess->isAccessible());
0607     }
0608 
0609     return true;
0610 }
0611 
0612 bool SolidDeviceEngine::updateInUse(const QString &udi)
0613 {
0614     Solid::Device device = m_devicemap.value(udi);
0615     if (!device.isValid()) {
0616         return false;
0617     }
0618 
0619     Solid::StorageAccess *storageaccess = device.as<Solid::StorageAccess>();
0620     if (!storageaccess) {
0621         return false;
0622     }
0623 
0624     if (storageaccess->isAccessible()) {
0625         setData(udi, kli18n("In Use").untranslatedText(), true);
0626     } else {
0627         Solid::StorageDrive *drive = getAncestorAs<Solid::StorageDrive>(Solid::Device(udi));
0628         if (drive) {
0629             setData(udi, kli18n("In Use").untranslatedText(), drive->isInUse());
0630         }
0631     }
0632 
0633     return true;
0634 }
0635 
0636 bool SolidDeviceEngine::updateSourceEvent(const QString &source)
0637 {
0638     bool update1 = updateStorageSpace(source);
0639     bool update2 = updateHardDiskTemperature(source);
0640     bool update3 = updateEmblems(source);
0641     bool update4 = updateInUse(source);
0642 
0643     return (update1 || update2 || update3 || update4);
0644 }
0645 
0646 void SolidDeviceEngine::deviceRemoved(const QString &udi)
0647 {
0648     // libsolid cannot notify us when an encrypted container is closed,
0649     // hence we trigger an update when a device contained in an encrypted container device dies
0650     const QString containerUdi = m_encryptedContainerMap.value(udi, QString());
0651 
0652     if (!containerUdi.isEmpty()) {
0653         forceUpdateAccessibility(containerUdi);
0654         m_encryptedContainerMap.remove(udi);
0655     }
0656 
0657     foreach (const QString &query, m_predicatemap.keys()) {
0658         m_predicatemap[query].removeAll(udi);
0659         setData(query, m_predicatemap[query]);
0660     }
0661 
0662     Solid::Device device(udi);
0663     if (device.is<Solid::StorageVolume>()) {
0664         Solid::StorageAccess *access = device.as<Solid::StorageAccess>();
0665         if (access) {
0666             disconnect(access, nullptr, this, nullptr);
0667         }
0668     } else if (device.is<Solid::OpticalDisc>()) {
0669         Solid::OpticalDrive *drive = getAncestorAs<Solid::OpticalDrive>(device);
0670         if (drive) {
0671             disconnect(drive, nullptr, this, nullptr);
0672         }
0673     }
0674 
0675     m_devicemap.remove(udi);
0676     removeSource(udi);
0677 }
0678 
0679 void SolidDeviceEngine::deviceChanged(const QString &udi, const QString &property, const QVariant &value)
0680 {
0681     setData(udi, property, value);
0682     updateSourceEvent(udi);
0683 }
0684 
0685 K_PLUGIN_CLASS_WITH_JSON(SolidDeviceEngine, "plasma-dataengine-soliddevice.json")
0686 
0687 #include "soliddeviceengine.moc"