summaryrefslogtreecommitdiffstats
path: root/xbmc/platform/linux/powermanagement
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/platform/linux/powermanagement')
-rw-r--r--xbmc/platform/linux/powermanagement/CMakeLists.txt17
-rw-r--r--xbmc/platform/linux/powermanagement/ConsoleUPowerSyscall.cpp50
-rw-r--r--xbmc/platform/linux/powermanagement/ConsoleUPowerSyscall.h23
-rw-r--r--xbmc/platform/linux/powermanagement/FallbackPowerSyscall.h26
-rw-r--r--xbmc/platform/linux/powermanagement/LinuxPowerSyscall.cpp65
-rw-r--r--xbmc/platform/linux/powermanagement/LinuxPowerSyscall.h18
-rw-r--r--xbmc/platform/linux/powermanagement/LogindUPowerSyscall.cpp342
-rw-r--r--xbmc/platform/linux/powermanagement/LogindUPowerSyscall.h52
-rw-r--r--xbmc/platform/linux/powermanagement/UPowerSyscall.cpp207
-rw-r--r--xbmc/platform/linux/powermanagement/UPowerSyscall.h61
10 files changed, 861 insertions, 0 deletions
diff --git a/xbmc/platform/linux/powermanagement/CMakeLists.txt b/xbmc/platform/linux/powermanagement/CMakeLists.txt
new file mode 100644
index 0000000..3694a79
--- /dev/null
+++ b/xbmc/platform/linux/powermanagement/CMakeLists.txt
@@ -0,0 +1,17 @@
+set(SOURCES LinuxPowerSyscall.cpp)
+
+set(HEADERS FallbackPowerSyscall.h
+ LinuxPowerSyscall.h)
+
+if(DBUS_FOUND)
+ list(APPEND SOURCES ConsoleUPowerSyscall.cpp
+ LogindUPowerSyscall.cpp
+ UPowerSyscall.cpp)
+ list(APPEND HEADERS ConsoleUPowerSyscall.h
+ LogindUPowerSyscall.h
+ UPowerSyscall.h)
+endif()
+
+if(SOURCES)
+ core_add_library(platform_linux_powermanagement)
+endif()
diff --git a/xbmc/platform/linux/powermanagement/ConsoleUPowerSyscall.cpp b/xbmc/platform/linux/powermanagement/ConsoleUPowerSyscall.cpp
new file mode 100644
index 0000000..6f04dd7
--- /dev/null
+++ b/xbmc/platform/linux/powermanagement/ConsoleUPowerSyscall.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "ConsoleUPowerSyscall.h"
+
+#include "utils/log.h"
+
+CConsoleUPowerSyscall::CConsoleUPowerSyscall()
+{
+ m_CanPowerdown = ConsoleKitMethodCall("CanStop");
+ m_CanReboot = ConsoleKitMethodCall("CanRestart");
+}
+
+bool CConsoleUPowerSyscall::Powerdown()
+{
+ CDBusMessage message("org.freedesktop.ConsoleKit", "/org/freedesktop/ConsoleKit/Manager", "org.freedesktop.ConsoleKit.Manager", "Stop");
+ return message.SendSystem() != NULL;
+}
+
+bool CConsoleUPowerSyscall::Reboot()
+{
+ CDBusMessage message("org.freedesktop.ConsoleKit", "/org/freedesktop/ConsoleKit/Manager", "org.freedesktop.ConsoleKit.Manager", "Restart");
+ return message.SendSystem() != NULL;
+}
+
+bool CConsoleUPowerSyscall::HasConsoleKitAndUPower()
+{
+ return CDBusUtil::TryMethodCall(DBUS_BUS_SYSTEM, "org.freedesktop.ConsoleKit", "/org/freedesktop/ConsoleKit/Manager", "org.freedesktop.ConsoleKit.Manager", "CanStop")
+ && HasUPower();
+}
+
+bool CConsoleUPowerSyscall::ConsoleKitMethodCall(const char *method)
+{
+ CDBusMessage message("org.freedesktop.ConsoleKit", "/org/freedesktop/ConsoleKit/Manager", "org.freedesktop.ConsoleKit.Manager", method);
+ DBusMessage *reply = message.SendSystem();
+ if (reply)
+ {
+ dbus_bool_t boolean = FALSE;
+
+ if (dbus_message_get_args (reply, NULL, DBUS_TYPE_BOOLEAN, &boolean, DBUS_TYPE_INVALID))
+ return boolean;
+ }
+
+ return false;
+}
diff --git a/xbmc/platform/linux/powermanagement/ConsoleUPowerSyscall.h b/xbmc/platform/linux/powermanagement/ConsoleUPowerSyscall.h
new file mode 100644
index 0000000..9fdd157
--- /dev/null
+++ b/xbmc/platform/linux/powermanagement/ConsoleUPowerSyscall.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "DBusUtil.h"
+#include "UPowerSyscall.h"
+
+class CConsoleUPowerSyscall : public CUPowerSyscall
+{
+public:
+ CConsoleUPowerSyscall();
+ bool Powerdown() override;
+ bool Reboot() override;
+ static bool HasConsoleKitAndUPower();
+private:
+ static bool ConsoleKitMethodCall(const char *method);
+};
diff --git a/xbmc/platform/linux/powermanagement/FallbackPowerSyscall.h b/xbmc/platform/linux/powermanagement/FallbackPowerSyscall.h
new file mode 100644
index 0000000..6988543
--- /dev/null
+++ b/xbmc/platform/linux/powermanagement/FallbackPowerSyscall.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "powermanagement/IPowerSyscall.h"
+
+class CFallbackPowerSyscall : public CPowerSyscallWithoutEvents
+{
+public:
+ bool Powerdown() override {return true; }
+ bool Suspend() override {return false; }
+ bool Hibernate() override {return false; }
+ bool Reboot() override {return true; }
+
+ bool CanPowerdown() override {return true; }
+ bool CanSuspend() override {return false; }
+ bool CanHibernate() override {return false; }
+ bool CanReboot() override {return true; }
+ int BatteryLevel() override {return 0; }
+};
diff --git a/xbmc/platform/linux/powermanagement/LinuxPowerSyscall.cpp b/xbmc/platform/linux/powermanagement/LinuxPowerSyscall.cpp
new file mode 100644
index 0000000..f3dbcdb
--- /dev/null
+++ b/xbmc/platform/linux/powermanagement/LinuxPowerSyscall.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "LinuxPowerSyscall.h"
+#include "FallbackPowerSyscall.h"
+#if defined(HAS_DBUS)
+#include "ConsoleUPowerSyscall.h"
+#include "LogindUPowerSyscall.h"
+#include "UPowerSyscall.h"
+#endif // HAS_DBUS
+
+#include <functional>
+#include <list>
+#include <memory>
+#include <utility>
+
+IPowerSyscall* CLinuxPowerSyscall::CreateInstance()
+{
+#if defined(HAS_DBUS)
+ std::unique_ptr<IPowerSyscall> bestPowerManager;
+ std::unique_ptr<IPowerSyscall> currPowerManager;
+ int bestCount = -1;
+ int currCount = -1;
+
+ std::list< std::pair< std::function<bool()>,
+ std::function<IPowerSyscall*()> > > powerManagers =
+ {
+ std::make_pair(CConsoleUPowerSyscall::HasConsoleKitAndUPower,
+ [] { return new CConsoleUPowerSyscall(); }),
+ std::make_pair(CLogindUPowerSyscall::HasLogind,
+ [] { return new CLogindUPowerSyscall(); }),
+ std::make_pair(CUPowerSyscall::HasUPower,
+ [] { return new CUPowerSyscall(); })
+ };
+ for(const auto& powerManager : powerManagers)
+ {
+ if (powerManager.first())
+ {
+ currPowerManager.reset(powerManager.second());
+ currCount = currPowerManager->CountPowerFeatures();
+ if (currCount > bestCount)
+ {
+ bestCount = currCount;
+ bestPowerManager = std::move(currPowerManager);
+ }
+ if (bestCount == IPowerSyscall::MAX_COUNT_POWER_FEATURES)
+ break;
+ }
+ }
+ if (bestPowerManager)
+ return bestPowerManager.release();
+ else
+#endif // HAS_DBUS
+ return new CFallbackPowerSyscall();
+}
+
+void CLinuxPowerSyscall::Register()
+{
+ IPowerSyscall::RegisterPowerSyscall(CLinuxPowerSyscall::CreateInstance);
+}
diff --git a/xbmc/platform/linux/powermanagement/LinuxPowerSyscall.h b/xbmc/platform/linux/powermanagement/LinuxPowerSyscall.h
new file mode 100644
index 0000000..b5cc218
--- /dev/null
+++ b/xbmc/platform/linux/powermanagement/LinuxPowerSyscall.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "powermanagement/IPowerSyscall.h"
+
+class CLinuxPowerSyscall
+{
+public:
+ static IPowerSyscall* CreateInstance();
+ static void Register();
+};
diff --git a/xbmc/platform/linux/powermanagement/LogindUPowerSyscall.cpp b/xbmc/platform/linux/powermanagement/LogindUPowerSyscall.cpp
new file mode 100644
index 0000000..bd04197
--- /dev/null
+++ b/xbmc/platform/linux/powermanagement/LogindUPowerSyscall.cpp
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 2012 Denis Yantarev
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "LogindUPowerSyscall.h"
+
+#include "utils/StringUtils.h"
+#include "utils/log.h"
+
+#include <string.h>
+
+#include <unistd.h>
+
+// logind DBus interface specification:
+// http://www.freedesktop.org/wiki/Software/Logind/logind
+//
+// Inhibitor Locks documentation:
+// http://www.freedesktop.org/wiki/Software/Logind/inhibit/
+
+#define LOGIND_DEST "org.freedesktop.login1"
+#define LOGIND_PATH "/org/freedesktop/login1"
+#define LOGIND_IFACE "org.freedesktop.login1.Manager"
+
+CLogindUPowerSyscall::CLogindUPowerSyscall()
+{
+ m_lowBattery = false;
+
+ CLog::Log(LOGINFO, "Selected Logind/UPower as PowerSyscall");
+
+ // Check if we have UPower. If not, we avoid any battery related operations.
+ CDBusMessage message("org.freedesktop.UPower", "/org/freedesktop/UPower", "org.freedesktop.UPower", "EnumerateDevices");
+ m_hasUPower = message.SendSystem() != NULL;
+
+ if (!m_hasUPower)
+ CLog::Log(LOGINFO, "LogindUPowerSyscall - UPower not found, battery information will not be available");
+
+ m_canPowerdown = LogindCheckCapability("CanPowerOff");
+ m_canReboot = LogindCheckCapability("CanReboot");
+ m_canHibernate = LogindCheckCapability("CanHibernate");
+ m_canSuspend = LogindCheckCapability("CanSuspend");
+
+ InhibitDelayLockSleep();
+
+ m_batteryLevel = 0;
+ if (m_hasUPower)
+ UpdateBatteryLevel();
+
+ if (!m_connection.Connect(DBUS_BUS_SYSTEM, true))
+ {
+ return;
+ }
+
+ CDBusError error;
+ dbus_connection_set_exit_on_disconnect(m_connection, false);
+ dbus_bus_add_match(m_connection, "type='signal',interface='org.freedesktop.login1.Manager',member='PrepareForSleep'", error);
+
+ if (!error && m_hasUPower)
+ dbus_bus_add_match(m_connection, "type='signal',interface='org.freedesktop.UPower',member='DeviceChanged'", error);
+
+ dbus_connection_flush(m_connection);
+
+ if (error)
+ {
+ error.Log("UPowerSyscall: Failed to attach to signal");
+ m_connection.Destroy();
+ }
+}
+
+CLogindUPowerSyscall::~CLogindUPowerSyscall()
+{
+ ReleaseDelayLockSleep();
+ ReleaseDelayLockShutdown();
+}
+
+bool CLogindUPowerSyscall::Powerdown()
+{
+ // delay shutdown so that the app can close properly
+ InhibitDelayLockShutdown();
+ return LogindSetPowerState("PowerOff");
+}
+
+bool CLogindUPowerSyscall::Reboot()
+{
+ return LogindSetPowerState("Reboot");
+}
+
+bool CLogindUPowerSyscall::Suspend()
+{
+ return LogindSetPowerState("Suspend");
+}
+
+bool CLogindUPowerSyscall::Hibernate()
+{
+ return LogindSetPowerState("Hibernate");
+}
+
+bool CLogindUPowerSyscall::CanPowerdown()
+{
+ return m_canPowerdown;
+}
+
+bool CLogindUPowerSyscall::CanSuspend()
+{
+ return m_canSuspend;
+}
+
+bool CLogindUPowerSyscall::CanHibernate()
+{
+ return m_canHibernate;
+}
+
+bool CLogindUPowerSyscall::CanReboot()
+{
+ return m_canReboot;
+}
+
+bool CLogindUPowerSyscall::HasLogind()
+{
+ // recommended method by systemd devs. The seats directory
+ // doesn't exist unless logind created it and therefore is running.
+ // see also https://mail.gnome.org/archives/desktop-devel-list/2013-March/msg00092.html
+ if (access("/run/systemd/seats/", F_OK) >= 0)
+ return true;
+
+ // on some environments "/run/systemd/seats/" doesn't exist, e.g. on flatpak. Try DBUS instead.
+ CDBusMessage message(LOGIND_DEST, LOGIND_PATH, LOGIND_IFACE, "ListSeats");
+ DBusMessage *reply = message.SendSystem();
+ if (!reply)
+ return false;
+
+ DBusMessageIter arrIter;
+ if (dbus_message_iter_init(reply, &arrIter) && dbus_message_iter_get_arg_type(&arrIter) == DBUS_TYPE_ARRAY)
+ {
+ DBusMessageIter structIter;
+ dbus_message_iter_recurse(&arrIter, &structIter);
+ if (dbus_message_iter_get_arg_type(&structIter) == DBUS_TYPE_STRUCT)
+ {
+ DBusMessageIter strIter;
+ dbus_message_iter_recurse(&structIter, &strIter);
+ if (dbus_message_iter_get_arg_type(&strIter) == DBUS_TYPE_STRING)
+ {
+ char *seat;
+ dbus_message_iter_get_basic(&strIter, &seat);
+ if (StringUtils::StartsWith(seat, "seat"))
+ {
+ CLog::Log(LOGDEBUG, "LogindUPowerSyscall::HasLogind - found seat: {}", seat);
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+bool CLogindUPowerSyscall::LogindSetPowerState(const char *state)
+{
+ CDBusMessage message(LOGIND_DEST, LOGIND_PATH, LOGIND_IFACE, state);
+ // The user_interaction boolean parameters can be used to control
+ // whether PolicyKit should interactively ask the user for authentication
+ // credentials if it needs to.
+ message.AppendArgument(false);
+ return message.SendSystem() != NULL;
+}
+
+bool CLogindUPowerSyscall::LogindCheckCapability(const char *capability)
+{
+ char *arg;
+ CDBusMessage message(LOGIND_DEST, LOGIND_PATH, LOGIND_IFACE, capability);
+ DBusMessage *reply = message.SendSystem();
+ if(reply && dbus_message_get_args(reply, NULL, DBUS_TYPE_STRING, &arg, DBUS_TYPE_INVALID))
+ {
+ // Returns one of "yes", "no" or "challenge". If "challenge" is
+ // returned the operation is available, but only after authorization.
+ return (strcmp(arg, "yes") == 0);
+ }
+ return false;
+}
+
+int CLogindUPowerSyscall::BatteryLevel()
+{
+ return m_batteryLevel;
+}
+
+void CLogindUPowerSyscall::UpdateBatteryLevel()
+{
+ char** source = NULL;
+ int length = 0;
+ double batteryLevelSum = 0;
+ int batteryCount = 0;
+
+ CDBusMessage message("org.freedesktop.UPower", "/org/freedesktop/UPower", "org.freedesktop.UPower", "EnumerateDevices");
+ DBusMessage *reply = message.SendSystem();
+
+ if (!reply)
+ return;
+
+ if (!dbus_message_get_args(reply, NULL, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &source, &length, DBUS_TYPE_INVALID))
+ {
+ CLog::Log(LOGWARNING, "LogindUPowerSyscall: failed to enumerate devices");
+ return;
+ }
+
+ for (int i = 0; i < length; i++)
+ {
+ CVariant properties = CDBusUtil::GetAll("org.freedesktop.UPower", source[i], "org.freedesktop.UPower.Device");
+ bool isRechargeable = properties["IsRechargeable"].asBoolean();
+
+ if (isRechargeable)
+ {
+ batteryCount++;
+ batteryLevelSum += properties["Percentage"].asDouble();
+ }
+ }
+
+ dbus_free_string_array(source);
+
+ if (batteryCount > 0)
+ {
+ m_batteryLevel = (int)(batteryLevelSum / (double)batteryCount);
+ m_lowBattery = CDBusUtil::GetVariant("org.freedesktop.UPower", "/org/freedesktop/UPower", "org.freedesktop.UPower", "OnLowBattery").asBoolean();
+ }
+}
+
+bool CLogindUPowerSyscall::PumpPowerEvents(IPowerEventsCallback *callback)
+{
+ bool result = false;
+ bool releaseLockSleep = false;
+
+ if (m_connection)
+ {
+ dbus_connection_read_write(m_connection, 0);
+ DBusMessagePtr msg(dbus_connection_pop_message(m_connection));
+
+ if (msg)
+ {
+ if (dbus_message_is_signal(msg.get(), "org.freedesktop.login1.Manager", "PrepareForSleep"))
+ {
+ dbus_bool_t arg;
+ // the boolean argument defines whether we are going to sleep (true) or just woke up (false)
+ dbus_message_get_args(msg.get(), NULL, DBUS_TYPE_BOOLEAN, &arg, DBUS_TYPE_INVALID);
+ CLog::Log(LOGDEBUG, "LogindUPowerSyscall: Received PrepareForSleep with arg {}", (int)arg);
+ if (arg)
+ {
+ callback->OnSleep();
+ releaseLockSleep = true;
+ }
+ else
+ {
+ callback->OnWake();
+ InhibitDelayLockSleep();
+ }
+
+ result = true;
+ }
+ else if (dbus_message_is_signal(msg.get(), "org.freedesktop.UPower", "DeviceChanged"))
+ {
+ bool lowBattery = m_lowBattery;
+ UpdateBatteryLevel();
+ if (m_lowBattery && !lowBattery)
+ callback->OnLowBattery();
+
+ result = true;
+ }
+ else
+ CLog::Log(LOGDEBUG, "LogindUPowerSyscall - Received unknown signal {}",
+ dbus_message_get_member(msg.get()));
+ }
+ }
+
+ if (releaseLockSleep)
+ ReleaseDelayLockSleep();
+
+ return result;
+}
+
+void CLogindUPowerSyscall::InhibitDelayLockSleep()
+{
+ m_delayLockSleepFd = InhibitDelayLock("sleep");
+}
+
+void CLogindUPowerSyscall::InhibitDelayLockShutdown()
+{
+ m_delayLockShutdownFd = InhibitDelayLock("shutdown");
+}
+
+int CLogindUPowerSyscall::InhibitDelayLock(const char *what)
+{
+#ifdef DBUS_TYPE_UNIX_FD
+ CDBusMessage message("org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager", "Inhibit");
+ message.AppendArgument(what); // what to inhibit
+ message.AppendArgument("XBMC"); // who
+ message.AppendArgument(""); // reason
+ message.AppendArgument("delay"); // mode
+
+ DBusMessage *reply = message.SendSystem();
+
+ if (!reply)
+ {
+ CLog::Log(LOGWARNING, "LogindUPowerSyscall - failed to inhibit {} delay lock", what);
+ return -1;
+ }
+
+ int delayLockFd;
+ if (!dbus_message_get_args(reply, NULL, DBUS_TYPE_UNIX_FD, &delayLockFd, DBUS_TYPE_INVALID))
+ {
+ CLog::Log(LOGWARNING, "LogindUPowerSyscall - failed to get inhibit file descriptor");
+ return -1;
+ }
+
+ CLog::Log(LOGDEBUG, "LogindUPowerSyscall - inhibit lock taken, fd {}", delayLockFd);
+ return delayLockFd;
+#else
+ CLog::Log(LOGWARNING, "LogindUPowerSyscall - inhibit lock support not compiled in");
+ return -1;
+#endif
+}
+
+void CLogindUPowerSyscall::ReleaseDelayLockSleep()
+{
+ ReleaseDelayLock(m_delayLockSleepFd, "sleep");
+ m_delayLockSleepFd = -1;
+}
+
+void CLogindUPowerSyscall::ReleaseDelayLockShutdown()
+{
+ ReleaseDelayLock(m_delayLockShutdownFd, "shutdown");
+ m_delayLockShutdownFd = -1;
+}
+
+void CLogindUPowerSyscall::ReleaseDelayLock(int lockFd, const char *what)
+{
+ if (lockFd != -1)
+ {
+ close(lockFd);
+ CLog::Log(LOGDEBUG, "LogindUPowerSyscall - delay lock {} released", what);
+ }
+}
diff --git a/xbmc/platform/linux/powermanagement/LogindUPowerSyscall.h b/xbmc/platform/linux/powermanagement/LogindUPowerSyscall.h
new file mode 100644
index 0000000..998e706
--- /dev/null
+++ b/xbmc/platform/linux/powermanagement/LogindUPowerSyscall.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2012 Denis Yantarev
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "DBusUtil.h"
+#include "powermanagement/IPowerSyscall.h"
+
+class CLogindUPowerSyscall : public CAbstractPowerSyscall
+{
+public:
+ CLogindUPowerSyscall();
+ ~CLogindUPowerSyscall() override;
+ bool Powerdown() override;
+ bool Suspend() override;
+ bool Hibernate() override;
+ bool Reboot() override;
+ bool CanPowerdown() override;
+ bool CanSuspend() override;
+ bool CanHibernate() override;
+ bool CanReboot() override;
+ int BatteryLevel() override;
+ bool PumpPowerEvents(IPowerEventsCallback *callback) override;
+ // we don't require UPower because everything except the battery level works fine without it
+ static bool HasLogind();
+private:
+ CDBusConnection m_connection;
+ bool m_canPowerdown;
+ bool m_canSuspend;
+ bool m_canHibernate;
+ bool m_canReboot;
+ bool m_hasUPower;
+ bool m_lowBattery;
+ int m_batteryLevel;
+ int m_delayLockSleepFd = -1; // file descriptor for the logind sleep delay lock
+ int m_delayLockShutdownFd = -1; // file descriptor for the logind powerdown delay lock
+ void UpdateBatteryLevel();
+ void InhibitDelayLockSleep();
+ void InhibitDelayLockShutdown();
+ int InhibitDelayLock(const char *what);
+ void ReleaseDelayLockSleep();
+ void ReleaseDelayLockShutdown();
+ void ReleaseDelayLock(int lockFd, const char *what);
+ static bool LogindSetPowerState(const char *state);
+ static bool LogindCheckCapability(const char *capability);
+};
diff --git a/xbmc/platform/linux/powermanagement/UPowerSyscall.cpp b/xbmc/platform/linux/powermanagement/UPowerSyscall.cpp
new file mode 100644
index 0000000..27ed2f4
--- /dev/null
+++ b/xbmc/platform/linux/powermanagement/UPowerSyscall.cpp
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "UPowerSyscall.h"
+
+#include "utils/log.h"
+
+CUPowerSource::CUPowerSource(const char *powerSource)
+{
+ if(powerSource == NULL)
+ m_powerSource = "";
+ else
+ m_powerSource = powerSource;
+
+ CVariant properties = CDBusUtil::GetAll("org.freedesktop.UPower", m_powerSource.c_str(), "org.freedesktop.UPower.Device");
+ m_isRechargeable = properties["IsRechargeable"].asBoolean();
+ Update();
+}
+
+CUPowerSource::~CUPowerSource() = default;
+
+void CUPowerSource::Update()
+{
+ CVariant properties = CDBusUtil::GetAll("org.freedesktop.UPower", m_powerSource.c_str(), "org.freedesktop.UPower.Device");
+ m_batteryLevel = properties["Percentage"].asDouble();
+}
+
+bool CUPowerSource::IsRechargeable()
+{
+ return m_isRechargeable;
+}
+
+double CUPowerSource::BatteryLevel()
+{
+ return m_batteryLevel;
+}
+
+CUPowerSyscall::CUPowerSyscall()
+{
+ CLog::Log(LOGINFO, "Selected UPower as PowerSyscall");
+
+ m_lowBattery = false;
+
+ //! @todo do not use dbus_connection_pop_message() that requires the use of a
+ //! private connection
+ if (m_connection.Connect(DBUS_BUS_SYSTEM, true))
+ {
+ dbus_connection_set_exit_on_disconnect(m_connection, false);
+
+ CDBusError error;
+ dbus_bus_add_match(m_connection, "type='signal',interface='org.freedesktop.UPower'", error);
+ dbus_connection_flush(m_connection);
+
+ if (error)
+ {
+ error.Log("UPower: Failed to attach to signal");
+ m_connection.Destroy();
+ }
+ }
+
+ m_CanPowerdown = false;
+ m_CanReboot = false;
+
+ UpdateCapabilities();
+
+ EnumeratePowerSources();
+}
+
+bool CUPowerSyscall::Powerdown()
+{
+ return false;
+}
+
+bool CUPowerSyscall::Suspend()
+{
+ // UPower 0.9.1 does not signal sleeping unless you tell that its about to sleep...
+ CDBusMessage aboutToSleepMessage("org.freedesktop.UPower", "/org/freedesktop/UPower", "org.freedesktop.UPower", "AboutToSleep");
+ aboutToSleepMessage.SendAsyncSystem();
+
+ CDBusMessage message("org.freedesktop.UPower", "/org/freedesktop/UPower", "org.freedesktop.UPower", "Suspend");
+ return message.SendAsyncSystem();
+}
+
+bool CUPowerSyscall::Hibernate()
+{
+ // UPower 0.9.1 does not signal sleeping unless you tell that its about to sleep...
+ CDBusMessage aboutToSleepMessage("org.freedesktop.UPower", "/org/freedesktop/UPower", "org.freedesktop.UPower", "AboutToSleep");
+ aboutToSleepMessage.SendAsyncSystem();
+
+ CDBusMessage message("org.freedesktop.UPower", "/org/freedesktop/UPower", "org.freedesktop.UPower", "Hibernate");
+ return message.SendAsyncSystem();
+}
+
+bool CUPowerSyscall::Reboot()
+{
+ return false;
+}
+
+bool CUPowerSyscall::CanPowerdown()
+{
+ return m_CanPowerdown;
+}
+
+bool CUPowerSyscall::CanSuspend()
+{
+ return m_CanSuspend;
+}
+
+bool CUPowerSyscall::CanHibernate()
+{
+ return m_CanHibernate;
+}
+
+bool CUPowerSyscall::CanReboot()
+{
+ return m_CanReboot;
+}
+
+int CUPowerSyscall::BatteryLevel()
+{
+ unsigned int nBatteryCount = 0;
+ double subCapacity = 0;
+ double batteryLevel = 0;
+
+ for (auto& itr : m_powerSources)
+ {
+ itr.Update();
+ if (itr.IsRechargeable())
+ {
+ nBatteryCount++;
+ subCapacity += itr.BatteryLevel();
+ }
+ }
+
+ if(nBatteryCount)
+ batteryLevel = subCapacity / (double)nBatteryCount;
+
+ return (int) batteryLevel;
+}
+
+void CUPowerSyscall::EnumeratePowerSources()
+{
+ CDBusMessage message("org.freedesktop.UPower", "/org/freedesktop/UPower", "org.freedesktop.UPower", "EnumerateDevices");
+ DBusMessage *reply = message.SendSystem();
+ if (reply)
+ {
+ char** source = NULL;
+ int length = 0;
+
+ if (dbus_message_get_args (reply, NULL, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &source, &length, DBUS_TYPE_INVALID))
+ {
+ for (int i = 0; i < length; i++)
+ {
+ m_powerSources.emplace_back(source[i]);
+ }
+
+ dbus_free_string_array(source);
+ }
+ }
+}
+
+bool CUPowerSyscall::HasUPower()
+{
+ return CDBusUtil::TryMethodCall(DBUS_BUS_SYSTEM, "org.freedesktop.UPower", "/org/freedesktop/UPower", "org.freedesktop.UPower", "EnumerateDevices");
+}
+
+bool CUPowerSyscall::PumpPowerEvents(IPowerEventsCallback *callback)
+{
+ bool result = false;
+
+ if (m_connection)
+ {
+ dbus_connection_read_write(m_connection, 0);
+ DBusMessagePtr msg(dbus_connection_pop_message(m_connection));
+
+ if (msg)
+ {
+ result = true;
+ if (dbus_message_is_signal(msg.get(), "org.freedesktop.UPower", "Sleeping"))
+ callback->OnSleep();
+ else if (dbus_message_is_signal(msg.get(), "org.freedesktop.UPower", "Resuming"))
+ callback->OnWake();
+ else if (dbus_message_is_signal(msg.get(), "org.freedesktop.UPower", "Changed"))
+ {
+ bool lowBattery = m_lowBattery;
+ UpdateCapabilities();
+ if (m_lowBattery && !lowBattery)
+ callback->OnLowBattery();
+ }
+ else
+ CLog::Log(LOGDEBUG, "UPower: Received an unknown signal {}",
+ dbus_message_get_member(msg.get()));
+ }
+ }
+ return result;
+}
+
+void CUPowerSyscall::UpdateCapabilities()
+{
+ m_CanSuspend = CDBusUtil::GetVariant("org.freedesktop.UPower", "/org/freedesktop/UPower", "org.freedesktop.UPower", "CanSuspend").asBoolean(false);
+ m_CanHibernate = CDBusUtil::GetVariant("org.freedesktop.UPower", "/org/freedesktop/UPower", "org.freedesktop.UPower", "CanHibernate").asBoolean(false);
+}
diff --git a/xbmc/platform/linux/powermanagement/UPowerSyscall.h b/xbmc/platform/linux/powermanagement/UPowerSyscall.h
new file mode 100644
index 0000000..aed08d8
--- /dev/null
+++ b/xbmc/platform/linux/powermanagement/UPowerSyscall.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "DBusUtil.h"
+#include "powermanagement/IPowerSyscall.h"
+
+#include <list>
+#include <string>
+
+class CUPowerSource
+{
+public:
+ CUPowerSource(const char *powerSource);
+ ~CUPowerSource();
+
+ void Update();
+ bool IsRechargeable();
+ double BatteryLevel();
+
+private:
+ std::string m_powerSource;
+ bool m_isRechargeable;
+ double m_batteryLevel;
+};
+
+class CUPowerSyscall : public CAbstractPowerSyscall
+{
+public:
+ CUPowerSyscall();
+ bool Powerdown() override;
+ bool Suspend() override;
+ bool Hibernate() override;
+ bool Reboot() override;
+ bool CanPowerdown() override;
+ bool CanSuspend() override;
+ bool CanHibernate() override;
+ bool CanReboot() override;
+ int BatteryLevel() override;
+ bool PumpPowerEvents(IPowerEventsCallback *callback) override;
+ static bool HasUPower();
+protected:
+ bool m_CanPowerdown;
+ bool m_CanSuspend;
+ bool m_CanHibernate;
+ bool m_CanReboot;
+
+ void UpdateCapabilities();
+private:
+ std::list<CUPowerSource> m_powerSources;
+ CDBusConnection m_connection;
+
+ bool m_lowBattery;
+ void EnumeratePowerSources();
+};