File indexing completed on 2024-04-28 05:36:16

0001 /*
0002     SPDX-FileCopyrightText: 2016 Martin Gräßlin <mgraesslin@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005 */
0006 
0007 #include "powerdevilpowermanagement.h"
0008 #include "powerdevil_debug.h"
0009 
0010 #include <QDBusConnection>
0011 #include <QDBusConnectionInterface>
0012 #include <QDBusServiceWatcher>
0013 
0014 namespace PowerDevil
0015 {
0016 static const QString s_fdoPowerService = QStringLiteral("org.freedesktop.PowerManagement");
0017 static const QString s_fdoPowerPath = QStringLiteral("/org/freedesktop/PowerManagement");
0018 
0019 class PowerManagementInstance : public PowerManagement
0020 {
0021     Q_OBJECT
0022 public:
0023     explicit PowerManagementInstance()
0024         : PowerManagement()
0025     {
0026     }
0027 };
0028 Q_GLOBAL_STATIC(PowerManagementInstance, s_instance)
0029 
0030 class PowerManagement::Private
0031 {
0032 public:
0033     Private(PowerManagement *q);
0034     void update();
0035     void setCanSuspend(bool set);
0036     void setCanHibernate(bool set);
0037     void setCanHybridSuspend(bool set);
0038     void setCanSuspendThenHibernate(bool set);
0039 
0040     bool serviceRegistered;
0041     bool canSuspend;
0042     bool canSuspendThenHibernate;
0043     bool canHibernate;
0044     bool canHybridSuspend;
0045     QScopedPointer<QDBusServiceWatcher> fdoPowerServiceWatcher;
0046 
0047 private:
0048     void updateProperty(const QString &dbusName, void (Private::*setter)(bool));
0049     PowerManagement *const q;
0050 };
0051 
0052 PowerManagement::Private::Private(PowerManagement *qq)
0053     : serviceRegistered(false)
0054     , canSuspend(false)
0055     , canSuspendThenHibernate(false)
0056     , canHibernate(false)
0057     , canHybridSuspend(false)
0058     , fdoPowerServiceWatcher(new QDBusServiceWatcher(s_fdoPowerService,
0059                                                      QDBusConnection::sessionBus(),
0060                                                      QDBusServiceWatcher::WatchForUnregistration | QDBusServiceWatcher::WatchForRegistration))
0061     , q(qq)
0062 {
0063 }
0064 
0065 void PowerManagement::Private::update()
0066 {
0067     serviceRegistered = true;
0068     updateProperty(QStringLiteral("CanSuspend"), &Private::setCanSuspend);
0069     updateProperty(QStringLiteral("CanSuspendThenHibernate"), &Private::setCanSuspendThenHibernate);
0070     updateProperty(QStringLiteral("CanHibernate"), &Private::setCanHibernate);
0071     updateProperty(QStringLiteral("CanHybridSuspend"), &Private::setCanHybridSuspend);
0072 }
0073 
0074 void PowerManagement::Private::updateProperty(const QString &dbusName, void (Private::*setter)(bool))
0075 {
0076     QDBusMessage message = QDBusMessage::createMethodCall(s_fdoPowerService, s_fdoPowerPath, s_fdoPowerService, dbusName);
0077     QDBusReply<bool> reply = QDBusConnection::sessionBus().call(message);
0078     if (!reply.isValid()) {
0079         return;
0080     }
0081 
0082     ((this)->*setter)(reply.value());
0083 }
0084 
0085 void PowerManagement::Private::setCanHibernate(bool set)
0086 {
0087     if (canHibernate == set) {
0088         return;
0089     }
0090     canHibernate = set;
0091     Q_EMIT q->canHibernateChanged();
0092 }
0093 
0094 void PowerManagement::Private::setCanSuspend(bool set)
0095 {
0096     if (canSuspend == set) {
0097         return;
0098     }
0099     canSuspend = set;
0100     Q_EMIT q->canSuspendChanged();
0101 }
0102 
0103 void PowerManagement::Private::setCanSuspendThenHibernate(bool set)
0104 {
0105     if (canSuspendThenHibernate == set) {
0106         return;
0107     }
0108     canSuspendThenHibernate = set;
0109     Q_EMIT q->canSuspendThenHibernateChanged();
0110 }
0111 
0112 void PowerManagement::Private::setCanHybridSuspend(bool set)
0113 {
0114     if (canHybridSuspend == set) {
0115         return;
0116     }
0117     canHybridSuspend = set;
0118     Q_EMIT q->canHybridSuspendChanged();
0119 }
0120 
0121 PowerManagement *PowerManagement::instance()
0122 {
0123     return s_instance;
0124 }
0125 
0126 PowerManagement::PowerManagement()
0127     : QObject()
0128     , d(new Private(this))
0129 {
0130     connect(d->fdoPowerServiceWatcher.data(), &QDBusServiceWatcher::serviceRegistered, this, [this] {
0131         d->update();
0132     });
0133     connect(d->fdoPowerServiceWatcher.data(), &QDBusServiceWatcher::serviceUnregistered, this, [this] {
0134         d->serviceRegistered = false;
0135         d->setCanSuspend(false);
0136         d->setCanHibernate(false);
0137         d->setCanHybridSuspend(false);
0138         d->setCanSuspendThenHibernate(false);
0139     });
0140 
0141     // check whether the service is registered
0142     QDBusMessage message = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.DBus"),
0143                                                           QStringLiteral("/"),
0144                                                           QStringLiteral("org.freedesktop.DBus"),
0145                                                           QStringLiteral("ListNames"));
0146     QDBusReply<QStringList> reply = QDBusConnection::sessionBus().call(message);
0147     if (!reply.isValid()) {
0148         return;
0149     }
0150     if (reply.value().contains(s_fdoPowerService)) {
0151         d->update();
0152     }
0153 }
0154 
0155 PowerManagement::~PowerManagement()
0156 {
0157 }
0158 
0159 void PowerManagement::suspend()
0160 {
0161     if (!d->serviceRegistered) {
0162         return;
0163     }
0164     if (!d->canSuspend) {
0165         return;
0166     }
0167     QDBusMessage message = QDBusMessage::createMethodCall(s_fdoPowerService, s_fdoPowerPath, s_fdoPowerService, QStringLiteral("Suspend"));
0168     QDBusConnection::sessionBus().asyncCall(message);
0169 }
0170 
0171 void PowerManagement::hibernate()
0172 {
0173     if (!d->serviceRegistered) {
0174         return;
0175     }
0176     if (!d->canHibernate) {
0177         return;
0178     }
0179     QDBusMessage message = QDBusMessage::createMethodCall(s_fdoPowerService, s_fdoPowerPath, s_fdoPowerService, QStringLiteral("Hibernate"));
0180     QDBusConnection::sessionBus().asyncCall(message);
0181 }
0182 
0183 void PowerManagement::hybridSuspend()
0184 {
0185     // FIXME: Whether there is a support of this mode?
0186 }
0187 
0188 void PowerManagement::suspendThenHibernate()
0189 {
0190     if (!d->serviceRegistered) {
0191         return;
0192     }
0193     if (!d->canSuspendThenHibernate) {
0194         return;
0195     }
0196     QDBusMessage message = QDBusMessage::createMethodCall(s_fdoPowerService, s_fdoPowerPath, s_fdoPowerService, QStringLiteral("SuspendThenHibernate"));
0197     QDBusConnection::sessionBus().asyncCall(message);
0198 }
0199 
0200 bool PowerManagement::isVirtualMachine()
0201 {
0202     if (!QDBusConnection::systemBus().interface()->isServiceRegistered(QStringLiteral("org.freedesktop.systemd1"))) {
0203         // can't check, fall back to assuming false
0204         return false;
0205     }
0206     QDBusMessage message = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.systemd1"),
0207                                                           QStringLiteral("/org/freedesktop/systemd1"),
0208                                                           QStringLiteral("org.freedesktop.DBus.Properties"),
0209                                                           QStringLiteral("Get"));
0210     message.setArguments({QStringLiteral("org.freedesktop.systemd1.Manager"), QStringLiteral("Virtualization")});
0211     QDBusReply<QDBusVariant> reply = QDBusConnection::systemBus().call(message);
0212     if (!reply.isValid() || reply.value().variant().isNull() || reply.value().variant().toString().isNull()) {
0213         qCWarning(POWERDEVIL) << "Failed to get property Virtualization from systemd1 DBus service:" << reply.error().message();
0214         return false;
0215     }
0216     // on bare-metal hardware this is the empty string, otherwise an identifier such as "kvm", "vmware", etc.
0217     return !reply.value().variant().toString().isEmpty();
0218 }
0219 
0220 bool PowerManagement::canSuspend() const
0221 {
0222     return d->canSuspend;
0223 }
0224 
0225 bool PowerManagement::canHibernate() const
0226 {
0227     return d->canHibernate;
0228 }
0229 
0230 bool PowerManagement::canHybridSuspend() const
0231 {
0232     return d->canHybridSuspend;
0233 }
0234 
0235 bool PowerManagement::canSuspendThenHibernate() const
0236 {
0237     return d->canSuspendThenHibernate;
0238 }
0239 
0240 } // namespace PowerDevil
0241 
0242 #include "powerdevilpowermanagement.moc"
0243 
0244 #include "moc_powerdevilpowermanagement.cpp"