1/*
2 SPDX-FileCopyrightText: 2010 Michael Zanetti <mzanetti@kde.org>
3 SPDX-FileCopyrightText: 2010 Lukas Tinkl <ltinkl@redhat.com>
4
5 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
6*/
7
8#include "upowerdevice.h"
9#include "upower.h"
10#include "upowerbattery.h"
11#include "upowerdeviceinterface.h"
12#include "upowergenericinterface.h"
13
14#include <solid/device.h>
15#include <solid/genericinterface.h>
16
17#include <QDBusConnection>
18#include <QDBusReply>
19#include <QStringList>
20
21using namespace Solid::Backends::UPower;
22
23UPowerDevice::UPowerDevice(const QString &udi)
24 : Solid::Ifaces::Device()
25 , m_udi(udi)
26{
27 QDBusConnection::systemBus().connect(QStringLiteral(UP_DBUS_SERVICE),
28 path: m_udi,
29 QStringLiteral("org.freedesktop.DBus.Properties"),
30 QStringLiteral("PropertiesChanged"),
31 receiver: this,
32 SLOT(onPropertiesChanged(QString, QVariantMap, QStringList)));
33
34 // TODO port this to Solid::Power, we can't link against kdelibs4support for this signal
35 // older upower versions not affected
36 QDBusConnection::systemBus().connect(QStringLiteral("org.freedesktop.login1"), //
37 QStringLiteral("/org/freedesktop/login1"),
38 QStringLiteral("org.freedesktop.login1.Manager"),
39 QStringLiteral("PrepareForSleep"),
40 receiver: this,
41 SLOT(login1Resuming(bool)));
42}
43
44UPowerDevice::~UPowerDevice()
45{
46}
47
48QObject *UPowerDevice::createDeviceInterface(const Solid::DeviceInterface::Type &type)
49{
50 if (!queryDeviceInterface(type)) {
51 return nullptr;
52 }
53
54 DeviceInterface *iface = nullptr;
55 switch (type) {
56 case Solid::DeviceInterface::GenericInterface:
57 iface = new GenericInterface(this);
58 break;
59 case Solid::DeviceInterface::Battery:
60 iface = new Battery(this);
61 break;
62 default:
63 break;
64 }
65 return iface;
66}
67
68bool UPowerDevice::queryDeviceInterface(const Solid::DeviceInterface::Type &type) const
69{
70 const UpDeviceKind uptype = static_cast<UpDeviceKind>(prop(QStringLiteral("Type")).toUInt());
71 switch (type) {
72 case Solid::DeviceInterface::GenericInterface:
73 return true;
74 case Solid::DeviceInterface::Battery:
75 switch (uptype) {
76 case UP_DEVICE_KIND_BATTERY:
77 case UP_DEVICE_KIND_UPS:
78 case UP_DEVICE_KIND_MOUSE:
79 case UP_DEVICE_KIND_KEYBOARD:
80 case UP_DEVICE_KIND_PDA:
81 case UP_DEVICE_KIND_PHONE:
82 case UP_DEVICE_KIND_TABLET:
83 case UP_DEVICE_KIND_GAMING_INPUT:
84 case UP_DEVICE_KIND_SPEAKERS:
85 case UP_DEVICE_KIND_HEADSET:
86 case UP_DEVICE_KIND_HEADPHONES:
87 case UP_DEVICE_KIND_BLUETOOTH_GENERIC:
88 case UP_DEVICE_KIND_TOUCHPAD:
89 case UP_DEVICE_KIND_WEARABLE:
90 return true;
91 case UP_DEVICE_KIND_UNKNOWN:
92 // There is currently no "Bluetooth battery" type, so check if it comes from Bluez
93 if (prop(QStringLiteral("NativePath")).toString().startsWith(s: QLatin1String("/org/bluez/"))) {
94 return true;
95 }
96 return false;
97 case UP_DEVICE_KIND_LINE_POWER:
98 case UP_DEVICE_KIND_MONITOR:
99 case UP_DEVICE_KIND_MEDIA_PLAYER:
100 case UP_DEVICE_KIND_COMPUTER:
101 case UP_DEVICE_KIND_LAST:
102 return false;
103 default:
104 Q_FALLTHROUGH();
105 }
106 default:
107 return false;
108 }
109}
110
111QStringList UPowerDevice::emblems() const
112{
113 return QStringList();
114}
115
116QString UPowerDevice::description() const
117{
118 if (queryDeviceInterface(type: Solid::DeviceInterface::Battery)) {
119 if (!batteryTechnology().isEmpty()) {
120 return tr(s: "%1 Battery", c: "%1 is battery technology").arg(a: batteryTechnology());
121 }
122 }
123 QString result = prop(QStringLiteral("Model")).toString();
124 if (result.isEmpty()) {
125 return vendor();
126 }
127 return result;
128}
129
130QString UPowerDevice::batteryTechnology() const
131{
132 const UpDeviceTechnology tech = static_cast<UpDeviceTechnology>(prop(QStringLiteral("Technology")).toUInt());
133 switch (tech) {
134 case UP_DEVICE_TECHNOLOGY_UNKNOWN:
135 return QString();
136 case UP_DEVICE_TECHNOLOGY_LITHIUM_ION:
137 return tr(s: "Lithium Ion", c: "battery technology");
138 case UP_DEVICE_TECHNOLOGY_LITHIUM_POLYMER:
139 return tr(s: "Lithium Polymer", c: "battery technology");
140 case UP_DEVICE_TECHNOLOGY_LITHIUM_IRON_PHOSPHATE:
141 return tr(s: "Lithium Iron Phosphate", c: "battery technology");
142 case UP_DEVICE_TECHNOLOGY_LEAD_ACID:
143 return tr(s: "Lead Acid", c: "battery technology");
144 case UP_DEVICE_TECHNOLOGY_NICKEL_CADMIUM:
145 return tr(s: "Nickel Cadmium", c: "battery technology");
146 case UP_DEVICE_TECHNOLOGY_NICKEL_METAL_HYDRIDE:
147 return tr(s: "Nickel Metal Hydride", c: "battery technology");
148 case UP_DEVICE_TECHNOLOGY_LAST:
149 return QString();
150 }
151 return QString();
152}
153
154QString UPowerDevice::icon() const
155{
156 if (queryDeviceInterface(type: Solid::DeviceInterface::Battery)) {
157 return QStringLiteral("battery");
158 } else {
159 return QString();
160 }
161}
162
163QString UPowerDevice::product() const
164{
165 QString result = prop(QStringLiteral("Model")).toString();
166
167 if (result.isEmpty()) {
168 result = description();
169 }
170
171 return result;
172}
173
174QString UPowerDevice::vendor() const
175{
176 return prop(QStringLiteral("Vendor")).toString();
177}
178
179QString UPowerDevice::udi() const
180{
181 return m_udi;
182}
183
184QString UPowerDevice::parentUdi() const
185{
186 return QStringLiteral(UP_UDI_PREFIX);
187}
188
189void UPowerDevice::checkCache(const QString &key) const
190{
191 if (m_cache.contains(key) || m_negativeCache.contains(str: key)) {
192 return;
193 }
194
195 loadCache();
196
197 if (m_cache.contains(key)) {
198 return;
199 }
200
201 QDBusMessage call =
202 QDBusMessage::createMethodCall(QStringLiteral(UP_DBUS_SERVICE), path: m_udi, QStringLiteral("org.freedesktop.DBus.Properties"), QStringLiteral("Get"));
203 call.setArguments({QStringLiteral(UP_DBUS_INTERFACE_DEVICE), key});
204 QDBusReply<QVariant> reply = QDBusConnection::systemBus().call(message: call);
205
206 if (reply.isValid()) {
207 m_cache[key] = reply.value();
208 } else {
209 m_negativeCache.append(t: key);
210 }
211}
212
213QVariant UPowerDevice::prop(const QString &key) const
214{
215 checkCache(key);
216 return m_cache.value(key);
217}
218
219bool UPowerDevice::propertyExists(const QString &key) const
220{
221 checkCache(key);
222 return m_cache.contains(key);
223}
224
225void UPowerDevice::loadCache() const
226{
227 QDBusMessage call =
228 QDBusMessage::createMethodCall(QStringLiteral(UP_DBUS_SERVICE), path: m_udi, QStringLiteral("org.freedesktop.DBus.Properties"), QStringLiteral("GetAll"));
229 call.setArguments({QStringLiteral(UP_DBUS_INTERFACE_DEVICE)});
230 QDBusReply<QVariantMap> reply = QDBusConnection::systemBus().call(message: call);
231
232 if (reply.isValid()) {
233 m_cache = reply.value();
234 m_cacheComplete = true;
235 } else {
236 m_cache.clear();
237 }
238}
239
240QMap<QString, QVariant> UPowerDevice::allProperties() const
241{
242 if (!m_cacheComplete) {
243 loadCache();
244 }
245
246 return m_cache;
247}
248
249void UPowerDevice::onPropertiesChanged(const QString &ifaceName, const QVariantMap &changedProps, const QStringList &invalidatedProps)
250{
251 if (ifaceName != QStringLiteral(UP_DBUS_INTERFACE_DEVICE))
252 return;
253
254 QMap<QString, int> changeMap;
255 for (auto it = changedProps.begin(); it != changedProps.end(); ++it) {
256 m_cache[it.key()] = it.value();
257 m_negativeCache.removeOne(t: it.key());
258 changeMap.insert(key: it.key(), value: Solid::GenericInterface::PropertyModified);
259 }
260 for (const auto &propName : invalidatedProps) {
261 m_cache.remove(key: propName);
262 m_negativeCache.removeOne(t: propName);
263 changeMap.insert(key: propName, value: Solid::GenericInterface::PropertyModified);
264 m_cacheComplete = false;
265 }
266 Q_EMIT propertyChanged(changes: changeMap);
267}
268
269void UPowerDevice::login1Resuming(bool active)
270{
271 // Nothing to do when going into sleep
272 if (active)
273 return;
274
275 QMap<QString, int> changeMap;
276 for (auto it = m_cache.begin(); it != m_cache.end(); ++it) {
277 if (it.value().isValid()) {
278 changeMap.insert(key: it.key(), value: Solid::GenericInterface::PropertyModified);
279 }
280 }
281 m_cache.clear();
282 m_negativeCache.clear();
283 m_cacheComplete = false;
284 Q_EMIT propertyChanged(changes: changeMap);
285}
286
287#include "moc_upowerdevice.cpp"
288

source code of solid/src/solid/devices/backends/upower/upowerdevice.cpp