Unity 8
AccountsService.cpp
1 /*
2  * Copyright (C) 2013 Canonical, Ltd.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 3.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program. If not, see <http://www.gnu.org/licenses/>.
15  *
16  * Author: Michael Terry <michael.terry@canonical.com>
17  */
18 
19 #include "AccountsService.h"
20 #include "AccountsServiceDBusAdaptor.h"
21 
22 #include <QDBusInterface>
23 #include <QFile>
24 #include <QStringList>
25 #include <QDebug>
26 
27 #define IFACE_ACCOUNTS_USER QStringLiteral("org.freedesktop.Accounts.User")
28 #define IFACE_LOCATION_HERE QStringLiteral("com.ubuntu.location.providers.here.AccountsService")
29 #define IFACE_UBUNTU_INPUT QStringLiteral("com.ubuntu.AccountsService.Input")
30 #define IFACE_UBUNTU_SECURITY QStringLiteral("com.ubuntu.AccountsService.SecurityPrivacy")
31 #define IFACE_UBUNTU_SECURITY_OLD QStringLiteral("com.ubuntu.touch.AccountsService.SecurityPrivacy")
32 #define IFACE_UNITY QStringLiteral("com.canonical.unity.AccountsService")
33 #define IFACE_UNITY_PRIVATE QStringLiteral("com.canonical.unity.AccountsService.Private")
34 
35 #define PROP_BACKGROUND_FILE QStringLiteral("BackgroundFile")
36 #define PROP_DEMO_EDGES QStringLiteral("demo-edges")
37 #define PROP_ENABLE_INDICATORS_WHILE_LOCKED QStringLiteral("EnableIndicatorsWhileLocked")
38 #define PROP_ENABLE_LAUNCHER_WHILE_LOCKED QStringLiteral("EnableLauncherWhileLocked")
39 #define PROP_FAILED_LOGINS QStringLiteral("FailedLogins")
40 #define PROP_LICENSE_ACCEPTED QStringLiteral("LicenseAccepted")
41 #define PROP_LICENSE_BASE_PATH QStringLiteral("LicenseBasePath")
42 #define PROP_MOUSE_CURSOR_SPEED QStringLiteral("MouseCursorSpeed")
43 #define PROP_MOUSE_DOUBLE_CLICK_SPEED QStringLiteral("MouseDoubleClickSpeed")
44 #define PROP_MOUSE_PRIMARY_BUTTON QStringLiteral("MousePrimaryButton")
45 #define PROP_MOUSE_SCROLL_SPEED QStringLiteral("MouseScrollSpeed")
46 #define PROP_PASSWORD_DISPLAY_HINT QStringLiteral("PasswordDisplayHint")
47 #define PROP_STATS_WELCOME_SCREEN QStringLiteral("StatsWelcomeScreen")
48 #define PROP_TOUCHPAD_CURSOR_SPEED QStringLiteral("TouchpadCursorSpeed")
49 #define PROP_TOUCHPAD_DISABLE_WHILE_TYPING QStringLiteral("TouchpadDisableWhileTyping")
50 #define PROP_TOUCHPAD_DISABLE_WITH_MOUSE QStringLiteral("TouchpadDisableWithMouse")
51 #define PROP_TOUCHPAD_DOUBLE_CLICK_SPEED QStringLiteral("TouchpadDoubleClickSpeed")
52 #define PROP_TOUCHPAD_PRIMARY_BUTTON QStringLiteral("TouchpadPrimaryButton")
53 #define PROP_TOUCHPAD_SCROLL_SPEED QStringLiteral("TouchpadScrollSpeed")
54 #define PROP_TOUCHPAD_TAP_TO_CLICK QStringLiteral("TouchpadTapToClick")
55 #define PROP_TOUCHPAD_TWO_FINGER_SCROLL QStringLiteral("TouchpadTwoFingerScroll")
56 
57 
58 QVariant primaryButtonConverter(const QVariant &value)
59 {
60  QString stringValue = value.toString();
61  if (stringValue == "left") {
62  return QVariant::fromValue(0);
63  } else if (stringValue == "right") {
64  return QVariant::fromValue(1); // Mir is less clear on this -- any non-zero value is the same
65  } else {
66  return QVariant::fromValue(0); // default to left
67  }
68 }
69 
70 AccountsService::AccountsService(QObject* parent, const QString &user)
71  : QObject(parent)
72  , m_service(new AccountsServiceDBusAdaptor(this))
73 {
74  m_unityInput = new QDBusInterface(QStringLiteral("com.canonical.Unity.Input"),
75  QStringLiteral("/com/canonical/Unity/Input"),
76  QStringLiteral("com.canonical.Unity.Input"),
77  QDBusConnection::SM_BUSNAME(), this);
78 
79  connect(m_service, &AccountsServiceDBusAdaptor::propertiesChanged, this, &AccountsService::onPropertiesChanged);
80  connect(m_service, &AccountsServiceDBusAdaptor::maybeChanged, this, &AccountsService::onMaybeChanged);
81 
82  registerProperty(IFACE_ACCOUNTS_USER, PROP_BACKGROUND_FILE, QStringLiteral("backgroundFileChanged"));
83  registerProperty(IFACE_LOCATION_HERE, PROP_LICENSE_ACCEPTED, QStringLiteral("hereEnabledChanged"));
84  registerProperty(IFACE_LOCATION_HERE, PROP_LICENSE_BASE_PATH, QStringLiteral("hereLicensePathChanged"));
85  registerProperty(IFACE_UBUNTU_SECURITY, PROP_ENABLE_LAUNCHER_WHILE_LOCKED, QStringLiteral("enableLauncherWhileLockedChanged"));
86  registerProperty(IFACE_UBUNTU_SECURITY, PROP_ENABLE_INDICATORS_WHILE_LOCKED, QStringLiteral("enableIndicatorsWhileLockedChanged"));
87  registerProperty(IFACE_UBUNTU_SECURITY, PROP_PASSWORD_DISPLAY_HINT, QStringLiteral("passwordDisplayHintChanged"));
88  registerProperty(IFACE_UBUNTU_SECURITY_OLD, PROP_STATS_WELCOME_SCREEN, QStringLiteral("statsWelcomeScreenChanged"));
89  registerProperty(IFACE_UNITY, PROP_DEMO_EDGES, QStringLiteral("demoEdgesChanged"));
90  registerProperty(IFACE_UNITY_PRIVATE, PROP_FAILED_LOGINS, QStringLiteral("failedLoginsChanged"));
91 
92  registerProxy(IFACE_UBUNTU_INPUT, PROP_MOUSE_CURSOR_SPEED,
93  m_unityInput, QStringLiteral("setMouseCursorSpeed"));
94  registerProxy(IFACE_UBUNTU_INPUT, PROP_MOUSE_DOUBLE_CLICK_SPEED,
95  m_unityInput, QStringLiteral("setMouseDoubleClickSpeed"));
96  registerProxy(IFACE_UBUNTU_INPUT, PROP_MOUSE_PRIMARY_BUTTON,
97  m_unityInput, QStringLiteral("setMousePrimaryButton"),
98  primaryButtonConverter);
99  registerProxy(IFACE_UBUNTU_INPUT, PROP_MOUSE_SCROLL_SPEED,
100  m_unityInput, QStringLiteral("setMouseScrollSpeed"));
101  registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_CURSOR_SPEED,
102  m_unityInput, QStringLiteral("setTouchpadCursorSpeed"));
103  registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_SCROLL_SPEED,
104  m_unityInput, QStringLiteral("setTouchpadScrollSpeed"));
105  registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_DISABLE_WHILE_TYPING,
106  m_unityInput, QStringLiteral("setTouchpadDisableWhileTyping"));
107  registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_DISABLE_WITH_MOUSE,
108  m_unityInput, QStringLiteral("setTouchpadDisableWithMouse"));
109  registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_DOUBLE_CLICK_SPEED,
110  m_unityInput, QStringLiteral("setTouchpadDoubleClickSpeed"));
111  registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_PRIMARY_BUTTON,
112  m_unityInput, QStringLiteral("setTouchpadPrimaryButton"),
113  primaryButtonConverter);
114  registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_TAP_TO_CLICK,
115  m_unityInput, QStringLiteral("setTouchpadTapToClick"));
116  registerProxy(IFACE_UBUNTU_INPUT, PROP_TOUCHPAD_TWO_FINGER_SCROLL,
117  m_unityInput, QStringLiteral("setTouchpadTwoFingerScroll"));
118 
119  setUser(!user.isEmpty() ? user : QString::fromUtf8(qgetenv("USER")));
120 }
121 
122 QString AccountsService::user() const
123 {
124  return m_user;
125 }
126 
127 void AccountsService::setUser(const QString &user)
128 {
129  if (user.isEmpty() || m_user == user)
130  return;
131 
132  bool wasEmpty = m_user.isEmpty();
133 
134  m_user = user;
135  Q_EMIT userChanged();
136 
137  // Do the first update synchronously, as a cheap way to block rendering
138  // until we have the right values on bootup.
139  refresh(!wasEmpty);
140 }
141 
142 bool AccountsService::demoEdges() const
143 {
144  auto value = getProperty(IFACE_UNITY, PROP_DEMO_EDGES);
145  return value.toBool();
146 }
147 
148 void AccountsService::setDemoEdges(bool demoEdges)
149 {
150  setProperty(IFACE_UNITY, PROP_DEMO_EDGES, demoEdges);
151 }
152 
153 bool AccountsService::enableLauncherWhileLocked() const
154 {
155  auto value = getProperty(IFACE_UBUNTU_SECURITY, PROP_ENABLE_LAUNCHER_WHILE_LOCKED);
156  return value.toBool();
157 }
158 
159 bool AccountsService::enableIndicatorsWhileLocked() const
160 {
161  auto value = getProperty(IFACE_UBUNTU_SECURITY, PROP_ENABLE_INDICATORS_WHILE_LOCKED);
162  return value.toBool();
163 }
164 
165 QString AccountsService::backgroundFile() const
166 {
167  auto value = getProperty(IFACE_ACCOUNTS_USER, PROP_BACKGROUND_FILE);
168  return value.toString();
169 }
170 
171 bool AccountsService::statsWelcomeScreen() const
172 {
173  auto value = getProperty(IFACE_UBUNTU_SECURITY_OLD, PROP_STATS_WELCOME_SCREEN);
174  return value.toBool();
175 }
176 
177 AccountsService::PasswordDisplayHint AccountsService::passwordDisplayHint() const
178 {
179  auto value = getProperty(IFACE_UBUNTU_SECURITY, PROP_PASSWORD_DISPLAY_HINT);
180  return (PasswordDisplayHint)value.toInt();
181 }
182 
183 bool AccountsService::hereEnabled() const
184 {
185  auto value = getProperty(IFACE_LOCATION_HERE, PROP_LICENSE_ACCEPTED);
186  return value.toBool();
187 }
188 
189 void AccountsService::setHereEnabled(bool enabled)
190 {
191  setProperty(IFACE_LOCATION_HERE, PROP_LICENSE_ACCEPTED, enabled);
192 }
193 
194 QString AccountsService::hereLicensePath() const
195 {
196  auto value = getProperty(IFACE_LOCATION_HERE, PROP_LICENSE_BASE_PATH);
197  QString hereLicensePath = value.toString();
198  if (hereLicensePath.isEmpty() || !QFile::exists(hereLicensePath))
199  hereLicensePath = QStringLiteral("");
200  return hereLicensePath;
201 }
202 
203 bool AccountsService::hereLicensePathValid() const
204 {
205  auto value = getProperty(IFACE_LOCATION_HERE, PROP_LICENSE_BASE_PATH);
206  return !value.toString().isNull();
207 }
208 
209 uint AccountsService::failedLogins() const
210 {
211  return getProperty(IFACE_UNITY_PRIVATE, PROP_FAILED_LOGINS).toUInt();
212 }
213 
214 void AccountsService::setFailedLogins(uint failedLogins)
215 {
216  setProperty(IFACE_UNITY_PRIVATE, PROP_FAILED_LOGINS, failedLogins);
217 }
218 
219 // ====================================================
220 // Everything below this line is generic helper methods
221 // ====================================================
222 
223 void AccountsService::emitChangedForProperty(const QString &interface, const QString &property)
224 {
225  QString signalName = m_properties[interface][property].signal;
226  QMetaObject::invokeMethod(this, signalName.toUtf8().data());
227 }
228 
229 QVariant AccountsService::getProperty(const QString &interface, const QString &property) const
230 {
231  return m_properties[interface][property].value;
232 }
233 
234 void AccountsService::setProperty(const QString &interface, const QString &property, const QVariant &value)
235 {
236  if (m_properties[interface][property].value != value) {
237  m_properties[interface][property].value = value;
238  m_service->setUserPropertyAsync(m_user, interface, property, value);
239  emitChangedForProperty(interface, property);
240  }
241 }
242 
243 void AccountsService::updateCache(const QString &interface, const QString &property, const QVariant &value)
244 {
245  PropertyInfo &info = m_properties[interface][property];
246 
247  if (info.proxyInterface) {
248  QVariant finalValue;
249  if (info.proxyConverter) {
250  finalValue = info.proxyConverter(value);
251  } else {
252  finalValue = value;
253  }
254  info.proxyInterface->asyncCall(info.proxyMethod, finalValue);
255  return; // don't bother saving a copy
256  }
257 
258  if (info.value != value) {
259  info.value = value;
260  emitChangedForProperty(interface, property);
261  }
262 }
263 
264 void AccountsService::updateProperty(const QString &interface, const QString &property)
265 {
266  QDBusPendingCall pendingReply = m_service->getUserPropertyAsync(m_user,
267  interface,
268  property);
269  QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingReply, this);
270 
271  connect(watcher, &QDBusPendingCallWatcher::finished,
272  this, [this, interface, property](QDBusPendingCallWatcher* watcher) {
273 
274  QDBusPendingReply<QVariant> reply = *watcher;
275  watcher->deleteLater();
276  if (reply.isError()) {
277  qWarning() << "Failed to get '" << property << "' property:" << reply.error().message();
278  return;
279  }
280 
281  updateCache(interface, property, reply.value());
282  });
283 }
284 
285 void AccountsService::updateAllProperties(const QString &interface, bool async)
286 {
287  QDBusPendingCall pendingReply = m_service->getAllPropertiesAsync(m_user,
288  interface);
289  QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingReply, this);
290 
291  connect(watcher, &QDBusPendingCallWatcher::finished,
292  this, [this, interface](QDBusPendingCallWatcher* watcher) {
293 
294  QDBusPendingReply< QHash<QString, QVariant> > reply = *watcher;
295  watcher->deleteLater();
296  if (reply.isError()) {
297  qWarning() << "Failed to get all properties for" << interface << ":" << reply.error().message();
298  return;
299  }
300 
301  auto valueHash = reply.value();
302  auto i = valueHash.constBegin();
303  while (i != valueHash.constEnd()) {
304  updateCache(interface, i.key(), i.value());
305  ++i;
306  }
307  });
308  if (!async) {
309  watcher->waitForFinished();
310  }
311 }
312 
313 void AccountsService::registerProxy(const QString &interface, const QString &property, QDBusInterface *iface, const QString &method, ProxyConverter converter)
314 {
315  registerProperty(interface, property, nullptr);
316 
317  m_properties[interface][property].proxyInterface = iface;
318  m_properties[interface][property].proxyMethod = method;
319  m_properties[interface][property].proxyConverter = converter;
320 }
321 
322 void AccountsService::registerProperty(const QString &interface, const QString &property, const QString &signal)
323 {
324  m_properties[interface][property] = PropertyInfo();
325  m_properties[interface][property].signal = signal;
326 }
327 
328 void AccountsService::onPropertiesChanged(const QString &user, const QString &interface, const QStringList &changed)
329 {
330  if (m_user != user) {
331  return;
332  }
333 
334  auto propHash = m_properties.value(interface);
335  auto i = propHash.constBegin();
336  while (i != propHash.constEnd()) {
337  if (changed.contains(i.key())) {
338  updateProperty(interface, i.key());
339  }
340  ++i;
341  }
342 }
343 
344 void AccountsService::onMaybeChanged(const QString &user)
345 {
346  if (m_user != user) {
347  return;
348  }
349 
350  // Any of the standard properties might have changed!
351  auto propHash = m_properties.value(IFACE_ACCOUNTS_USER);
352  auto i = propHash.constBegin();
353  while (i != propHash.constEnd()) {
354  updateProperty(IFACE_ACCOUNTS_USER, i.key());
355  ++i;
356  }
357 }
358 
359 void AccountsService::refresh(bool async)
360 {
361  auto i = m_properties.constBegin();
362  while (i != m_properties.constEnd()) {
363  updateAllProperties(i.key(), async);
364  ++i;
365  }
366 }