File indexing completed on 2024-04-28 16:55:15

0001 /********************************************************************
0002 Copyright 2016  Martin Gräßlin <mgraesslin@kde.org>
0003 
0004 This library is free software; you can redistribute it and/or
0005 modify it under the terms of the GNU Lesser General Public
0006 License as published by the Free Software Foundation; either
0007 version 2.1 of the License, or (at your option) version 3, or any
0008 later version accepted by the membership of KDE e.V. (or its
0009 successor approved by the membership of KDE e.V.), which shall
0010 act as a proxy defined in Section 6 of version 3 of the license.
0011 
0012 This library is distributed in the hope that it will be useful,
0013 but WITHOUT ANY WARRANTY; without even the implied warranty of
0014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0015 Lesser General Public License for more details.
0016 
0017 You should have received a copy of the GNU Lesser General Public
0018 License along with this library.  If not, see <http://www.gnu.org/licenses/>.
0019 *********************************************************************/
0020 
0021 #include "powerdevilpowermanagement.h"
0022 #include "powerdevil_debug.h"
0023 
0024 #include <QDBusConnection>
0025 #include <QDBusConnectionInterface>
0026 #include <QDBusServiceWatcher>
0027 
0028 namespace PowerDevil {
0029 
0030 static const QString s_fdoPowerService = QStringLiteral("org.freedesktop.PowerManagement");
0031 static const QString s_fdoPowerPath = QStringLiteral("/org/freedesktop/PowerManagement");
0032 
0033 class PowerManagementInstance : public PowerManagement
0034 {
0035     Q_OBJECT
0036 public:
0037     explicit PowerManagementInstance() : PowerManagement() {}
0038 };
0039 Q_GLOBAL_STATIC(PowerManagementInstance, s_instance)
0040 
0041 class PowerManagement::Private
0042 {
0043 public:
0044     Private(PowerManagement *q);
0045     void update();
0046     void setCanSuspend(bool set);
0047     void setCanHibernate(bool set);
0048     void setCanHybridSuspend(bool set);
0049     void setCanSuspendThenHibernate(bool set);
0050 
0051     bool serviceRegistered;
0052     bool canSuspend;
0053     bool canSuspendThenHibernate;
0054     bool canHibernate;
0055     bool canHybridSuspend;
0056     QScopedPointer<QDBusServiceWatcher> fdoPowerServiceWatcher;
0057 
0058 private:
0059     void updateProperty(const QString &dbusName, void (Private::*setter)(bool));
0060     PowerManagement *const q;
0061 };
0062 
0063 PowerManagement::Private::Private(PowerManagement *qq)
0064     : serviceRegistered(false)
0065     , canSuspend(false)
0066     , canSuspendThenHibernate(false)
0067     , canHibernate(false)
0068     , canHybridSuspend(false)
0069     , fdoPowerServiceWatcher(new QDBusServiceWatcher(s_fdoPowerService, QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForUnregistration | QDBusServiceWatcher::WatchForRegistration))
0070     , q(qq)
0071 {
0072 }
0073 
0074 void PowerManagement::Private::update()
0075 {
0076     serviceRegistered = true;
0077     updateProperty(QStringLiteral("CanSuspend"), &Private::setCanSuspend);
0078     updateProperty(QStringLiteral("CanSuspendThenHibernate"), &Private::setCanSuspendThenHibernate);
0079     updateProperty(QStringLiteral("CanHibernate"), &Private::setCanHibernate);
0080     updateProperty(QStringLiteral("CanHybridSuspend"), &Private::setCanHybridSuspend);
0081 }
0082 
0083 void PowerManagement::Private::updateProperty(const QString &dbusName, void (Private::*setter)(bool))
0084 {
0085     QDBusMessage message = QDBusMessage::createMethodCall(s_fdoPowerService,
0086                                                           s_fdoPowerPath,
0087                                                           s_fdoPowerService,
0088                                                           dbusName);
0089     QDBusReply<bool> reply = QDBusConnection::sessionBus().call(message);
0090     if (!reply.isValid()) {
0091         return;
0092     }
0093 
0094     ((this)->*setter)(reply.value());
0095 }
0096 
0097 void PowerManagement::Private::setCanHibernate(bool set)
0098 {
0099     if (canHibernate == set) {
0100         return;
0101     }
0102     canHibernate = set;
0103     Q_EMIT q->canHibernateChanged();
0104 }
0105 
0106 void PowerManagement::Private::setCanSuspend(bool set)
0107 {
0108     if (canSuspend == set) {
0109         return;
0110     }
0111     canSuspend = set;
0112     Q_EMIT q->canSuspendChanged();
0113 }
0114 
0115 void PowerManagement::Private::setCanSuspendThenHibernate(bool set)
0116 {
0117     if (canSuspendThenHibernate == set) {
0118         return;
0119     }
0120     canSuspendThenHibernate = set;
0121     Q_EMIT q->canSuspendThenHibernateChanged();
0122 }
0123 
0124 void PowerManagement::Private::setCanHybridSuspend(bool set)
0125 {
0126     if (canHybridSuspend == set) {
0127         return;
0128     }
0129     canHybridSuspend = set;
0130     Q_EMIT q->canHybridSuspendChanged();
0131 }
0132 
0133 PowerManagement *PowerManagement::instance()
0134 {
0135     return s_instance;
0136 }
0137 
0138 PowerManagement::PowerManagement()
0139     : QObject()
0140     , d(new Private(this))
0141 {
0142     connect(d->fdoPowerServiceWatcher.data(), &QDBusServiceWatcher::serviceRegistered, this, [this] { d->update(); });
0143     connect(d->fdoPowerServiceWatcher.data(), &QDBusServiceWatcher::serviceUnregistered, this,
0144         [this] {
0145             d->serviceRegistered = false;
0146             d->setCanSuspend(false);
0147             d->setCanHibernate(false);
0148             d->setCanHybridSuspend(false);
0149             d->setCanSuspendThenHibernate(false);
0150         }
0151     );
0152 
0153     // check whether the service is registered
0154     QDBusMessage message = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.DBus"),
0155                                                           QStringLiteral("/"),
0156                                                           QStringLiteral("org.freedesktop.DBus"),
0157                                                           QStringLiteral("ListNames"));
0158     QDBusReply<QStringList> reply = QDBusConnection::sessionBus().call(message);
0159     if (!reply.isValid()) {
0160         return;
0161     }
0162     if (reply.value().contains(s_fdoPowerService)) {
0163         d->update();
0164     }
0165 }
0166 
0167 PowerManagement::~PowerManagement()
0168 {
0169 }
0170 
0171 void PowerManagement::suspend()
0172 {
0173     if (!d->serviceRegistered) {
0174         return;
0175     }
0176     if (!d->canSuspend) {
0177         return;
0178     }
0179     QDBusMessage message = QDBusMessage::createMethodCall(s_fdoPowerService,
0180                                                           s_fdoPowerPath,
0181                                                           s_fdoPowerService,
0182                                                           QStringLiteral("Suspend"));
0183     QDBusConnection::sessionBus().asyncCall(message);
0184 }
0185 
0186 void PowerManagement::hibernate()
0187 {
0188     if (!d->serviceRegistered) {
0189         return;
0190     }
0191     if (!d->canHibernate) {
0192         return;
0193     }
0194     QDBusMessage message = QDBusMessage::createMethodCall(s_fdoPowerService,
0195                                                           s_fdoPowerPath,
0196                                                           s_fdoPowerService,
0197                                                           QStringLiteral("Hibernate"));
0198     QDBusConnection::sessionBus().asyncCall(message);
0199 }
0200 
0201 void PowerManagement::hybridSuspend()
0202 {
0203     // FIXME: Whether there is a support of this mode?
0204 }
0205 
0206 void PowerManagement::suspendThenHibernate()
0207 {
0208     if (!d->serviceRegistered) {
0209         return;
0210     }
0211     if (!d->canSuspendThenHibernate) {
0212         return;
0213     }
0214     QDBusMessage message = QDBusMessage::createMethodCall(s_fdoPowerService,
0215                                                           s_fdoPowerPath,
0216                                                           s_fdoPowerService,
0217                                                           QStringLiteral("SuspendThenHibernate"));
0218     QDBusConnection::sessionBus().asyncCall(message);
0219 }
0220 
0221 bool PowerManagement::isVirtualMachine()
0222 {
0223     if (!QDBusConnection::systemBus().interface()->isServiceRegistered(QStringLiteral("org.freedesktop.systemd1"))) {
0224         // can't check, fall back to assuming false
0225         return false;
0226     }
0227     QDBusMessage message = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.systemd1"),
0228                                                           QStringLiteral("/org/freedesktop/systemd1"),
0229                                                           QStringLiteral("org.freedesktop.DBus.Properties"),
0230                                                           QStringLiteral("Get"));
0231     message.setArguments({QStringLiteral("org.freedesktop.systemd1.Manager"), QStringLiteral("Virtualization")});
0232     QDBusReply<QDBusVariant> reply = QDBusConnection::systemBus().call(message);
0233     if (!reply.isValid() || reply.value().variant().isNull() || reply.value().variant().toString().isNull()) {
0234         qCWarning(POWERDEVIL) << "Failed to get property Virtualization from systemd1 DBus service:" << reply.error().message();
0235         return false;
0236     }
0237     /* on bare-metal hardware this is the empty string, otherwise an identifier such as "kvm", "vmware", etc. */
0238     return !reply.value().variant().toString().isEmpty();
0239 }
0240 
0241 bool PowerManagement::canSuspend() const
0242 {
0243     return d->canSuspend;
0244 }
0245 
0246 bool PowerManagement::canHibernate() const
0247 {
0248     return d->canHibernate;
0249 }
0250 
0251 bool PowerManagement::canHybridSuspend() const
0252 {
0253     return d->canHybridSuspend;
0254 }
0255 
0256 bool PowerManagement::canSuspendThenHibernate() const
0257 {
0258     return d->canSuspendThenHibernate;
0259 }
0260 
0261 } // namespace PowerDevil
0262 
0263 #include "powerdevilpowermanagement.moc"