diff options
Diffstat (limited to 'xbmc/platform/linux/powermanagement')
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(); +}; |