MythTV  master
mythpowerdbus.cpp
Go to the documentation of this file.
1 // MythTV
2 #include "mythlogging.h"
3 #include "mythpowerdbus.h"
4 
5 // Std
6 #include <unistd.h>
7 #include <algorithm>
8 
9 // Qt
10 #include <QDBusReply>
11 #include <QDBusUnixFileDescriptor>
12 
13 #define LOC QString("PowerDBus: ")
14 
15 #define FREE_SERVICE (QString("org.freedesktop."))
16 #define FREE_PATH (QString("/org/freedesktop/"))
17 #define UPOWER (QString("UPower"))
18 #define LOGIN1 (QString("login1"))
19 #define UPOWER_SERVICE (FREE_SERVICE + UPOWER)
20 #define UPOWER_PATH (FREE_PATH + UPOWER)
21 #define UPOWER_INTERFACE (UPOWER_SERVICE)
22 #define LOGIN1_SERVICE (FREE_SERVICE + LOGIN1)
23 #define LOGIN1_PATH (FREE_PATH + LOGIN1)
24 #define LOGIN1_INTERFACE (LOGIN1_SERVICE + QString(".Manager"))
25 
47 {
48  QMutexLocker locker(&s_lock);
49  static bool s_available = false;
50  static bool s_checked = false;
51  if (!s_checked)
52  {
53  s_checked = true;
54  auto* upower = new QDBusInterface(
55  UPOWER_SERVICE, UPOWER_PATH, UPOWER_INTERFACE, QDBusConnection::systemBus());
56  auto* login1 = new QDBusInterface(
57  LOGIN1_SERVICE, LOGIN1_PATH, LOGIN1_INTERFACE, QDBusConnection::systemBus());
58  s_available = upower->isValid() || login1->isValid();
59  delete upower;
60  delete login1;
61  }
62  return s_available;
63 }
64 
66 {
67  m_delayTimer.setSingleShot(true);
70 }
71 
73 {
75  ReleaseLock();
77  LOG(VB_GENERAL, LOG_INFO, LOC + "Closing interfaces");
78  delete m_upowerInterface;
79  delete m_logindInterface;
80 }
81 
83 {
84  // create interfaces
87 
89  {
90  if (!m_upowerInterface->isValid())
91  {
92  delete m_upowerInterface;
93  m_upowerInterface = nullptr;
94  }
95  }
96 
98  {
99  if (!m_logindInterface->isValid())
100  {
101  delete m_logindInterface;
102  m_logindInterface = nullptr;
103  }
104  }
105 
106  if (!m_upowerInterface)
107  LOG(VB_GENERAL, LOG_ERR, LOC + "No UPower interface. Unable to monitor battery state");
108  if (!m_logindInterface)
109  LOG(VB_GENERAL, LOG_WARNING, LOC + "No login1 interface. Cannot change system power state");
110 
111  // listen for sleep/wake events
112  if (m_logindInterface)
113  {
114  // delay system requests
116 
117  if (!m_bus.connect(LOGIN1_SERVICE, LOGIN1_PATH, LOGIN1_INTERFACE, "PrepareForSleep",
118  this, SLOT(DBusSuspending(bool))))
119  {
120  LOG(VB_GENERAL, LOG_WARNING, LOC + "Failed to listen for sleep events");
121  }
122  if (!m_bus.connect(LOGIN1_SERVICE, LOGIN1_PATH, LOGIN1_INTERFACE, "PrepareForShutdown",
123  this, SLOT(DBusShuttingDown(bool))))
124  {
125  LOG(VB_GENERAL, LOG_WARNING, LOC + "Failed to listen for shutdown events");
126  }
127  }
128 
129  // populate power devices (i.e. batteries)
130  if (m_upowerInterface)
131  {
132  QDBusReply<QList<QDBusObjectPath> > response =
133  m_upowerInterface->call(QLatin1String("EnumerateDevices"));
134  if (response.isValid())
135  {
136  QList devices = response.value();
137  for (const auto& device : std::as_const(devices))
138  DeviceAdded(device);
139  }
140 
141  if (!m_bus.connect(UPOWER_SERVICE, UPOWER_PATH, UPOWER_SERVICE, "Changed", this, SLOT(Changed())))
142  {
143  LOG(VB_GENERAL, LOG_ERR, "Failed to register for Changed");
144  }
145 
146  if (!m_bus.connect(UPOWER_SERVICE, UPOWER_PATH, UPOWER_SERVICE, "DeviceChanged", "o",
147  this, SLOT(DeviceChanged(QDBusObjectPath))))
148  {
149  LOG(VB_GENERAL, LOG_ERR, "Failed to register for DeviceChanged");
150  }
151 
152  if (!m_bus.connect(UPOWER_SERVICE, UPOWER_PATH, UPOWER_SERVICE, "DeviceAdded", "o",
153  this, SLOT(DeviceAdded(QDBusObjectPath))))
154  {
155  LOG(VB_GENERAL, LOG_ERR, "Failed to register for DeviceAdded");
156  }
157 
158  if (!m_bus.connect(UPOWER_SERVICE, UPOWER_PATH, UPOWER_SERVICE, "DeviceRemoved", "o",
159  this, SLOT(DeviceRemoved(QDBusObjectPath))))
160  {
161  LOG(VB_GENERAL, LOG_ERR, "Failed to register for DeviceRemoved");
162  }
163  }
164 
165  Changed();
166  MythPower::Init();
167 }
168 
169 bool MythPowerDBus::DoFeature(bool Delayed)
170 {
171  if (!m_logindInterface ||
172  ((m_features & m_scheduledFeature) == 0U) ||
173  (m_scheduledFeature == 0U))
174  return false;
175 
176  if (!Delayed)
177  ReleaseLock();
178  switch (m_scheduledFeature)
179  {
180  case FeatureSuspend: m_logindInterface->call("Suspend", false); break;
181  case FeatureShutdown: m_logindInterface->call("PowerOff", false); break;
182  case FeatureHibernate: m_logindInterface->call("Hibernate", false); break;
183  case FeatureRestart: m_logindInterface->call("Reboot", false); break;
184  case FeatureHybridSleep: m_logindInterface->call("HybridSleep", false); break;
185  case FeatureNone: return false;
186  }
187  return true;
188 }
189 
190 void MythPowerDBus::DBusSuspending(bool Stopping)
191 {
192  if (Stopping)
193  {
195  return;
196 
197  if (UpdateStatus())
198  return;
199 
201  return;
202  }
203  DidWakeUp();
204 }
205 
207 {
208  if (Stopping)
209  {
211  return;
212 
213  if (UpdateStatus())
214  return;
215 
217  return;
218  }
219  DidWakeUp(); // after hibernate?
220 }
221 
223 {
225  return false;
226 
228  QVariant property = m_logindInterface->property("PreparingForShutdown");
229  if (property.isValid() && property.toBool())
231 
232  if (!feature)
233  {
234  property = m_logindInterface->property("PreparingForSleep");
235  if (property.isValid() && property.toBool())
237  }
238 
239  if (!feature)
240  return false;
241 
243 
244  // TODO It would be nice to check the ScheduledShutdown property to confirm
245  // the time available before shutdown/suspend but Qt doesn't like the type
246  // definition and aborts. Requires custom handling that is beyond the wit of this man.
247  LOG(VB_GENERAL, LOG_INFO, LOC + QString("System will %1").arg(FeatureToString(feature)));
248 
249  // Attempt to delay the action.
250 
251  // NB we don't care about user preference here. We are giving
252  // MythTV interested components an opportunity to cleanup before
253  // an externally initiated shutdown/suspend
255  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Trying to delay system %1 for %2 seconds")
256  .arg(FeatureToString(feature)).arg(delay.count()));
257  m_delayTimer.start(delay);
258 
259  switch (feature)
260  {
261  case FeatureSuspend: emit WillSuspend(delay); break;
262  case FeatureShutdown: emit WillShutDown(delay); break;
263  default: break;
264  }
265 
266  return true;
267 }
268 
270 {
271  QMutexLocker locker(&s_lock);
272  m_delayTimer.stop();
275 }
276 
290 bool MythPowerDBus::ScheduleFeature(enum Feature Type, std::chrono::seconds Delay)
291 {
292  if (!MythPower::ScheduleFeature(Type, Delay))
293  return false;
294 
295  if (Delay < 1s)
296  return true;
297 
298  // try and use ScheduleShutdown as it gives the system the opportunity
299  // to inhibit shutdown and just plays nicely with other users - not least
300  // any mythbackend that is running. Suspend/hibernate are not supported.
301  if (m_logindInterface && (Type == FeatureShutdown || Type == FeatureRestart))
302  {
303  auto time = nowAsDuration<std::chrono::milliseconds>();
304  if (time > 0ms)
305  {
306  std::chrono::milliseconds millisecs = time + Delay;
307  QLatin1String type;
308  switch (Type)
309  {
310  case FeatureShutdown: type = QLatin1String("poweroff"); break;
311  case FeatureRestart: type = QLatin1String("reboot"); break;
312  default: break;
313  }
314  QDBusReply<void> reply =
315  m_logindInterface->call(QLatin1String("ScheduleShutdown"), type,
316  static_cast<qint64>(millisecs.count()));
317 
318  if (reply.isValid() && !reply.error().isValid())
319  {
320  // cancel the default handling.
321  m_featureTimer.stop();
322  LOG(VB_GENERAL, LOG_INFO, LOC + QString("%1 scheduled via logind")
323  .arg(FeatureToString(Type)));
324  m_delayTimer.start(Delay);
325  }
326  else
327  {
328  LOG(VB_GENERAL, LOG_DEBUG, LOC +
329  QString("Failed to schedule %1 - falling back to default behaviour")
330  .arg(FeatureToString(Type)));
331  LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Error %1 Message %2")
332  .arg(reply.error().name(), reply.error().message()));
333  }
334  }
335  }
336  else if (Type == FeatureSuspend)
337  {
338  // no logind scheduling but intiate suspend now and retain lock until ready
339  m_featureTimer.stop();
340  m_delayTimer.start(Delay);
341  DoFeature(true);
342  }
343 
344  return true;
345 }
346 
349 {
350  QMutexLocker locker(&s_lock);
351 
352  if (m_delayTimer.isActive())
353  m_delayTimer.stop();
355 }
356 
358 {
359  QMutexLocker locker(&s_lock);
361  UpdateBattery();
362 }
363 
364 void MythPowerDBus::DeviceAdded(const QDBusObjectPath& Device)
365 {
366  {
367  QMutexLocker locker(&s_lock);
368  if (m_batteries.contains(Device.path()))
369  return;
370  m_batteries.insert(Device.path(), RetrieveBatteryLevel(Device.path()));
371  }
372  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Added UPower.Device '%1'").arg(Device.path()));
373  UpdateBattery();
374 }
375 
376 void MythPowerDBus::DeviceRemoved(const QDBusObjectPath& Device)
377 {
378  {
379  QMutexLocker locker(&s_lock);
380  if (!m_batteries.contains(Device.path()))
381  return;
382  m_batteries.remove(Device.path());
383  }
384  LOG(VB_GENERAL, LOG_INFO, QString("Removed UPower.Device '%1'").arg(Device.path()));
385  UpdateBattery();
386 }
387 
393 void MythPowerDBus::DeviceChanged(const QDBusObjectPath& Device)
394 {
395  {
396  QMutexLocker locker(&s_lock);
397  if (!m_batteries.contains(Device.path()))
398  return;
399  m_batteries[Device.path()] = RetrieveBatteryLevel(Device.path());
400  }
401  UpdateBattery();
402 }
403 
405 {
407  m_onBattery = false;
408 
409  if (m_logindInterface)
410  {
411  QDBusReply<QString> cansuspend = m_logindInterface->call(QLatin1String("CanSuspend"));
412  if (cansuspend.isValid() && cansuspend.value() == "yes")
414  QDBusReply<QString> canshutdown = m_logindInterface->call(QLatin1String("CanPowerOff"));
415  if (canshutdown.isValid() && canshutdown.value() == "yes")
417  QDBusReply<QString> canrestart = m_logindInterface->call(QLatin1String("CanReboot"));
418  if (canrestart.isValid() && canrestart.value() == "yes")
420  QDBusReply<QString> canhibernate = m_logindInterface->call(QLatin1String("CanHibernate"));
421  if (canhibernate.isValid() && canhibernate.value() == "yes")
423  QDBusReply<QString> canhybrid = m_logindInterface->call(QLatin1String("CanHybridSleep"));
424  if (canhybrid.isValid() && canhybrid.value() == "yes")
426 
427  QVariant delay = m_logindInterface->property("InhibitDelayMaxUSec");
428  if (delay.isValid())
429  {
430  auto value = std::chrono::microseconds(delay.toUInt());
431  m_maxSupportedDelay = duration_cast<std::chrono::seconds>(value);
432  LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Max inhibit delay: %1seconds")
433  .arg(m_maxSupportedDelay.count()));
434  }
435  }
436 }
437 
439 {
440  int newlevel = UnknownPower;
441 
442  if (m_onBattery)
443  {
444  QMutexLocker locker(&s_lock);
445 
446  qreal total = 0;
447  int count = 0;
448 
449  // take an average (who has more than 1 battery?)
450  for (int level : std::as_const(m_batteries))
451  {
452  if (level >= 0 && level <= 100)
453  {
454  count++;
455  total += static_cast<qreal>(level);
456  }
457  }
458 
459  if (count > 0)
460  newlevel = lround(total / count);
461  }
462 
464  {
465  QVariant acpower = m_logindInterface->property("OnExternalPower");
466  if (acpower.isValid() && acpower.toBool())
467  newlevel = ACPower;
468  }
469 
470  PowerLevelChanged(newlevel);
471 }
472 
473 int MythPowerDBus::RetrieveBatteryLevel(const QString &Path)
474 {
475  QDBusInterface interface(UPOWER_SERVICE, Path, UPOWER_SERVICE + ".Device", m_bus);
476 
477  if (interface.isValid())
478  {
479  QVariant battery = interface.property("IsRechargeable");
480  if (battery.isValid() && battery.toBool())
481  {
482  QVariant percent = interface.property("Percentage");
483  if (percent.isValid())
484  {
485  int result = static_cast<int>(lroundf(percent.toFloat() * 100.0F));
486  if (result >= 0 && result <= 100)
487  {
488  m_onBattery = true;
489  return result;
490  }
491  }
492  }
493  else
494  {
495  QVariant type = interface.property("Type");
496  if (type.isValid())
497  {
498  QString typestr = type.toString();
499  if (typestr == "Line Power")
500  return ACPower;
501  if (typestr == "Ups")
502  return UPS;
503  }
504  }
505  }
506  return UnknownPower;
507 }
508 
520 void MythPowerDBus::AcquireLock(Features Types)
521 {
522  QMutexLocker locker(&s_lock);
523 
524  if (m_lockHandle > -1)
525  {
526  LOG(VB_GENERAL, LOG_WARNING, LOC + "Already hold delay lock");
527  ReleaseLock();
528  }
529 
530  QStringList types;
531  if (Types.testFlag(FeatureSuspend)) types << "sleep";
532  if (Types.testFlag(FeatureShutdown)) types << "shutdown";
533  if (types.isEmpty())
534  {
535  LOG(VB_GENERAL, LOG_ERR, LOC + "Unknown delay requests");
536  return;
537  }
538 
539  QDBusReply<QDBusUnixFileDescriptor> reply =
540  m_logindInterface->call(QLatin1String("Inhibit"), types.join(":").toLocal8Bit().constData(),
541  QLatin1String("MythTV"), QLatin1String(""), QLatin1String("delay"));
542  if (!reply.isValid())
543  {
544  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Failed to delay %1: %2")
545  .arg(types.join(","), reply.error().message()));
546  m_lockHandle = -1;
547  return;
548  }
549 
550  m_lockHandle = dup(reply.value().fileDescriptor());
551  LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Acquired delay FD: %1").arg(m_lockHandle));
552 }
553 
560 {
561  QMutexLocker locker(&s_lock);
562  if (m_lockHandle < 0)
563  return;
564 
565  if (m_scheduledFeature)
567 
568  LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Releasing delay FD: %1").arg(m_lockHandle));
570  m_lockHandle = -1;
571 }
hardwareprofile.smolt.timeout
float timeout
Definition: smolt.py:102
MythPowerDBus::UpdateBattery
void UpdateBattery(void)
Definition: mythpowerdbus.cpp:438
MythPowerDBus::DeviceChanged
void DeviceChanged(const QDBusObjectPath &Device)
Update power device state.
Definition: mythpowerdbus.cpp:393
MythPowerDBus::UpdateStatus
bool UpdateStatus(void)
Definition: mythpowerdbus.cpp:222
MythPower::FeatureToString
static QString FeatureToString(enum Feature Type)
Definition: mythpower.cpp:240
MythPower::m_scheduledFeature
Feature m_scheduledFeature
Definition: mythpower.h:92
MythPower::FeatureHappening
virtual void FeatureHappening(Feature Spontaneous=FeatureNone)
Signal to the rest of MythTV that the given feature will happen now.
Definition: mythpower.cpp:298
MythPowerDBus::DidWakeUp
void DidWakeUp(void) override
Definition: mythpowerdbus.cpp:269
MythPower::FeatureIsEquivalent
static bool FeatureIsEquivalent(Feature First, Feature Second)
Definition: mythpower.cpp:254
MythPowerDBus::DBusShuttingDown
void DBusShuttingDown(bool Stopping)
Definition: mythpowerdbus.cpp:206
MythPowerDBus::m_delayTimer
QTimer m_delayTimer
Definition: mythpowerdbus.h:55
MythPowerDBus::ScheduleFeature
bool ScheduleFeature(enum Feature Type, std::chrono::seconds Delay) override
Schedule a MythTV initiated power feature.
Definition: mythpowerdbus.cpp:290
types
static const struct wl_interface * types[]
Definition: idle_inhibit_unstable_v1.c:39
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MythPower::m_maxRequestedDelay
std::chrono::seconds m_maxRequestedDelay
Definition: mythpower.h:94
MythPower::WillSuspend
void WillSuspend(std::chrono::milliseconds MilliSeconds=0ms)
Device
A device containing images (ie. USB stick, CD, storage group etc)
Definition: imagemanager.cpp:35
MythPowerDBus::DeviceAdded
void DeviceAdded(const QDBusObjectPath &Device)
Definition: mythpowerdbus.cpp:364
MythPower::m_maxSupportedDelay
std::chrono::seconds m_maxSupportedDelay
Definition: mythpower.h:95
MythPowerDBus::ReleaseLock
void ReleaseLock(void)
Release our inhibition lock.
Definition: mythpowerdbus.cpp:559
MythPowerDBus::DoFeature
bool DoFeature(bool Delayed=false) override
Definition: mythpowerdbus.cpp:169
MythPower::FeatureHibernate
@ FeatureHibernate
Definition: mythpower.h:40
close
#define close
Definition: compat.h:43
MythPowerDBus::Init
void Init(void) override
Definition: mythpowerdbus.cpp:82
MythPower::ScheduleFeature
virtual bool ScheduleFeature(enum Feature Type, std::chrono::seconds Delay)
Definition: mythpower.cpp:271
MythPowerDBus::m_onBattery
bool m_onBattery
Definition: mythpowerdbus.h:49
MythPowerDBus::m_bus
QDBusConnection m_bus
Definition: mythpowerdbus.h:51
MythPowerDBus::m_upowerInterface
QDBusInterface * m_upowerInterface
Definition: mythpowerdbus.h:52
MythPowerDBus::IsAvailable
static bool IsAvailable(void)
Static check for DBus interfaces that support some form of power management.
Definition: mythpowerdbus.cpp:46
mythlogging.h
MythPower::Feature
Feature
Definition: mythpower.h:35
MythPowerDBus::DBusSuspending
void DBusSuspending(bool Stopping)
Definition: mythpowerdbus.cpp:190
MythPowerDBus::CancelFeature
void CancelFeature(void) override
This is untested.
Definition: mythpowerdbus.cpp:348
MythPowerDBus::m_batteries
QMap< QString, int > m_batteries
Definition: mythpowerdbus.h:50
UPOWER_SERVICE
#define UPOWER_SERVICE
Definition: mythpowerdbus.cpp:19
MythPower::FeatureNone
@ FeatureNone
Definition: mythpower.h:37
mythpowerdbus.h
feature
static const std::array< featureStruct, 7 > feature
Definition: audiooutputsettings.cpp:434
MythPowerDBus::AcquireLock
void AcquireLock(Features Types)
Acquire an inhibition lock for logind power events.
Definition: mythpowerdbus.cpp:520
MythPowerDBus::Changed
void Changed(void)
Definition: mythpowerdbus.cpp:357
LOGIN1_PATH
#define LOGIN1_PATH
Definition: mythpowerdbus.cpp:23
MythPower::PowerLevelChanged
void PowerLevelChanged(int Level)
Definition: mythpower.cpp:342
UPOWER_PATH
#define UPOWER_PATH
Definition: mythpowerdbus.cpp:20
MythPowerDBus::DeviceRemoved
void DeviceRemoved(const QDBusObjectPath &Device)
Definition: mythpowerdbus.cpp:376
MythPower::m_features
Features m_features
Definition: mythpower.h:91
MythPowerDBus::m_lockHandle
int m_lockHandle
Definition: mythpowerdbus.h:54
clamp
static eu8 clamp(eu8 value, eu8 low, eu8 high)
Definition: pxsup2dast.c:204
MythPower::FeatureSuspend
@ FeatureSuspend
Definition: mythpower.h:39
MythPower::CancelFeature
virtual void CancelFeature(void)
This is untested as it is currently not clear whether it is useful.
Definition: mythpower.cpp:219
MythPowerDBus::UpdateProperties
void UpdateProperties(void)
Definition: mythpowerdbus.cpp:404
MythPower::s_lock
static QRecursiveMutex s_lock
Definition: mythpower.h:76
LOGIN1_SERVICE
#define LOGIN1_SERVICE
Definition: mythpowerdbus.cpp:22
MythPowerDBus::RetrieveBatteryLevel
int RetrieveBatteryLevel(const QString &Path)
Definition: mythpowerdbus.cpp:473
MythPower::FeatureRestart
@ FeatureRestart
Definition: mythpower.h:41
MythPower::FeatureShutdown
@ FeatureShutdown
Definition: mythpower.h:38
UPOWER_INTERFACE
#define UPOWER_INTERFACE
Definition: mythpowerdbus.cpp:21
MythPower::WillShutDown
void WillShutDown(std::chrono::milliseconds MilliSeconds=0ms)
MythPower::FeatureHybridSleep
@ FeatureHybridSleep
Definition: mythpower.h:42
LOGIN1_INTERFACE
#define LOGIN1_INTERFACE
Definition: mythpowerdbus.cpp:24
MythPower::m_featureTimer
QTimer m_featureTimer
Definition: mythpower.h:96
MythPowerDBus::~MythPowerDBus
~MythPowerDBus() override
Definition: mythpowerdbus.cpp:72
MythPower::DidWakeUp
virtual void DidWakeUp(void)
Definition: mythpower.cpp:325
MythPowerDBus::MythPowerDBus
MythPowerDBus()
Definition: mythpowerdbus.cpp:65
MythPowerDBus::m_logindInterface
QDBusInterface * m_logindInterface
Definition: mythpowerdbus.h:53
MythPower::ACPower
@ ACPower
Definition: mythpower.h:27
LOC
#define LOC
Definition: mythpowerdbus.cpp:13
MythPower::UnknownPower
@ UnknownPower
Definition: mythpower.h:31
MythPower::UPS
@ UPS
Definition: mythpower.h:26
MythPower::Init
virtual void Init(void)
Definition: mythpower.cpp:127