on xenial help sign in
Fix a race between Qml loading and DBus registration that caused problems when jenkins tried to unlock the phone.

There is a small window of opportunity between when a Qml plugin registers on DBus and when the Qml is finished loading and hooking up signals. If the unlock-device script happens to hit that window, it will report success (DBus method was called), but the screen won't unlock (Qml never saw signal). An easy fix is to just wait for the eventloop to be idle before registering on DBus. That's what I've done in this branch. In addition to fixing the proximate problem (LightDM), I've also fixed other Qml plugins that do the same thing. Fixes: 1370644 Approved by: Michael Zanetti

commit89dbba88e99c7c6ffdf576555e68206b1bdefc65 authorMichael Terry <michael.terry@canonical.com> dateThu 30 Oct 2014, 21:40 (12 years ago) parent324c3e772c4cda
28 files changed+337−130
plugins/LightDM/CMakeLists.txt+2 plugins/LightDM/DBusGreeter.cpp+3−24 plugins/LightDM/DBusGreeter.h+3−6 plugins/LightDM/DBusGreeterList.cpp+3−24 plugins/LightDM/DBusGreeterList.h+3−6 plugins/LightDM/plugin.cpp+2−9 plugins/Unity/DashCommunicator/dbusdashcommunicatorservice.cpp+1−5 plugins/Unity/DashCommunicator/dbusdashcommunicatorservice.h+2−2 plugins/Unity/Launcher/CMakeLists.txt+5−1 plugins/Unity/Launcher/dbusinterface.cpp+3−36 plugins/Unity/Launcher/dbusinterface.h+2−5 plugins/Unity/Session/CMakeLists.txt+5−1 plugins/Unity/Session/dbusunitysessionservice.cpp+2−8 plugins/Unity/Session/dbusunitysessionservice.h+2−2 qml/Shell.qml+13 src/libunity8-private/CMakeLists.txt+2 src/libunity8-private/unitydbusobject.cpp+86 src/libunity8-private/unitydbusobject.h+46 src/libunity8-private/unitydbusvirtualobject.cpp+83 src/libunity8-private/unitydbusvirtualobject.h+47 tests/mocks/LightDM/CMakeLists.txt+2 tests/plugins/Unity/Launcher/CMakeLists.txt+5−1 tests/plugins/Unity/Launcher/launchermodeltest.cpp+1 tests/plugins/Unity/Session/CMakeLists.txt+4 tests/plugins/Unity/Session/sessionbackendtest.cpp+1 tests/qmltests/tst_Shell.qml+3 tests/qmltests/tst_ShellWithPin.qml+3 tests/qmltests/tst_TabletShell.qml+3
plugins/LightDM/CMakeLists.txt+2
@@ -10,6 +10,7 @@ include_directories(
1010 ${CMAKE_SOURCE_DIR}/plugins/Utils
1111 ${CMAKE_SOURCE_DIR}/tests/mocks/LightDM
1212 #${LIBLIGHTDM_INCLUDE_DIRS}
13+ ${libunity8-private_SOURCE_DIR}
1314 ${LIBUSERMETRICSOUTPUT_INCLUDE_DIRS}
1415)
1516
@@ -28,6 +29,7 @@ add_library(LightDM-qml MODULE
2829
2930target_link_libraries(LightDM-qml
3031 MockLightDM-demo
32+ unity8-private
3133# TODO: Once we split out a separate greeter process, uncomment these lines
3234# ${LIBLIGHTDM_LDFLAGS}
3335 ${LIBUSERMETRICSOUTPUT_LDFLAGS}
plugins/LightDM/DBusGreeter.cpp+3−24
@@ -20,11 +20,9 @@
2020#include <QDBusMessage>
2121#include <QStringList>
2222
23−DBusGreeter::DBusGreeter(Greeter *greeter, const QDBusConnection &connection, const QString &path)
24− : QObject(greeter),
25− m_greeter(greeter),
26− m_connection(connection),
27− m_path(path)
23+DBusGreeter::DBusGreeter(Greeter *greeter, const QString &path)
24+ : UnityDBusObject(path, "com.canonical.UnityGreeter", true, greeter),
25+ m_greeter(greeter)
2826{
2927 connect(m_greeter, SIGNAL(isActiveChanged()), this, SLOT(isActiveChangedHandler()));
3028}
@@ -49,22 +47,3 @@ void DBusGreeter::isActiveChangedHandler()
4947 notifyPropertyChanged("IsActive", isActive());
5048 Q_EMIT isActiveChanged();
5149}
52−
53−// Manually emit a PropertiesChanged signal over DBus, because QtDBus
54−// doesn't do it for us on Q_PROPERTIES, oddly enough.
55−void DBusGreeter::notifyPropertyChanged(const QString& propertyName, const QVariant &value)
56−{
57− QDBusMessage message;
58− QVariantMap changedProps;
59−
60− changedProps.insert(propertyName, value);
61−
62− message = QDBusMessage::createSignal(m_path,
63− "org.freedesktop.DBus.Properties",
64− "PropertiesChanged");
65− message << "com.canonical.UnityGreeter";
66− message << changedProps;
67− message << QStringList();
68−
69− m_connection.send(message);
70−}
plugins/LightDM/DBusGreeter.h+3−6
@@ -17,15 +17,15 @@
1717#ifndef UNITY_DBUSGREETER_H
1818#define UNITY_DBUSGREETER_H
1919
20+#include "unitydbusobject.h"
2021#include <QDBusConnection>
21−#include <QObject>
2222
2323class Greeter;
2424
2525/** This is an internal class used to talk with the indicators.
2626 */
2727
28−class DBusGreeter : public QObject
28+class DBusGreeter : public UnityDBusObject
2929{
3030 Q_OBJECT
3131 Q_CLASSINFO("D-Bus Interface", "com.canonical.UnityGreeter")
@@ -33,7 +33,7 @@ class DBusGreeter : public QObject
3333 Q_PROPERTY(bool IsActive READ isActive NOTIFY isActiveChanged) // since 14.10
3434
3535public:
36− explicit DBusGreeter(Greeter *greeter, const QDBusConnection &connection, const QString &path);
36+ explicit DBusGreeter(Greeter *greeter, const QString &path);
3737
3838 bool isActive() const;
3939 Q_SCRIPTABLE void ShowGreeter(); // temporary, until we split the greeter again
@@ -44,12 +44,9 @@ Q_SIGNALS:
4444
4545private Q_SLOTS:
4646 void isActiveChangedHandler();
47− void notifyPropertyChanged(const QString &propertyName, const QVariant &value);
4847
4948private:
5049 Greeter *m_greeter;
51− QDBusConnection m_connection;
52− QString m_path;
5350};
5451
5552#endif
plugins/LightDM/DBusGreeterList.cpp+3−24
@@ -20,11 +20,9 @@
2020#include <QDBusMessage>
2121#include <QStringList>
2222
23−DBusGreeterList::DBusGreeterList(Greeter *greeter, const QDBusConnection &connection, const QString &path)
24− : QObject(greeter),
25− m_greeter(greeter),
26− m_connection(connection),
27− m_path(path)
23+DBusGreeterList::DBusGreeterList(Greeter *greeter, const QString &path)
24+ : UnityDBusObject(path, "com.canonical.UnityGreeter", true, greeter),
25+ m_greeter(greeter)
2826{
2927 connect(m_greeter, SIGNAL(authenticationUserChanged(const QString &)), this, SLOT(authenticationUserChangedHandler(const QString &)));
3028 connect(m_greeter, SIGNAL(promptlessChanged()), this, SLOT(promptlessChangedHandler()));
@@ -56,22 +54,3 @@ void DBusGreeterList::promptlessChangedHandler()
5654 notifyPropertyChanged("EntryIsLocked", entryIsLocked());
5755 Q_EMIT entryIsLockedChanged();
5856}
59−
60−// Manually emit a PropertiesChanged signal over DBus, because QtDBus
61−// doesn't do it for us on Q_PROPERTIES, oddly enough.
62−void DBusGreeterList::notifyPropertyChanged(const QString& propertyName, const QVariant &value)
63−{
64− QDBusMessage message;
65− QVariantMap changedProps;
66−
67− changedProps.insert(propertyName, value);
68−
69− message = QDBusMessage::createSignal(m_path,
70− "org.freedesktop.DBus.Properties",
71− "PropertiesChanged");
72− message << "com.canonical.UnityGreeter.List";
73− message << changedProps;
74− message << QStringList();
75−
76− m_connection.send(message);
77−}
plugins/LightDM/DBusGreeterList.h+3−6
@@ -17,15 +17,15 @@
1717#ifndef UNITY_DBUSGREETERLIST_H
1818#define UNITY_DBUSGREETERLIST_H
1919
20+#include "unitydbusobject.h"
2021#include <QDBusConnection>
21−#include <QObject>
2222
2323class Greeter;
2424
2525/** This is an internal class used to talk with the indicators.
2626 */
2727
28−class DBusGreeterList : public QObject
28+class DBusGreeterList : public UnityDBusObject
2929{
3030 Q_OBJECT
3131 Q_CLASSINFO("D-Bus Interface", "com.canonical.UnityGreeter.List")
@@ -34,7 +34,7 @@ class DBusGreeterList : public QObject
3434 Q_PROPERTY(bool EntryIsLocked READ entryIsLocked NOTIFY entryIsLockedChanged) // since 14.04
3535
3636public:
37− explicit DBusGreeterList(Greeter *greeter, const QDBusConnection &connection, const QString &path);
37+ explicit DBusGreeterList(Greeter *greeter, const QString &path);
3838
3939 Q_SCRIPTABLE void SetActiveEntry(const QString &entry); // since 13.04
4040 Q_SCRIPTABLE QString GetActiveEntry() const; // since 13.10
@@ -49,12 +49,9 @@ Q_SIGNALS:
4949private Q_SLOTS:
5050 void authenticationUserChangedHandler(const QString &user);
5151 void promptlessChangedHandler();
52− void notifyPropertyChanged(const QString &propertyName, const QVariant &value);
5352
5453private:
5554 Greeter *m_greeter;
56− QDBusConnection m_connection;
57− QString m_path;
5855};
5956
6057#endif
plugins/LightDM/plugin.cpp+2−9
@@ -30,21 +30,14 @@
3030#include <QDBusConnection>
3131#include <QtQml/qqml.h>
3232
33−static const char* GREETER_LIST_DBUS_PATH = "/list";
34−static const char* GREETER_DBUS_SERVICE = "com.canonical.UnityGreeter";
35−
3633static QObject *greeter_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
3734{
3835 Q_UNUSED(engine)
3936 Q_UNUSED(scriptEngine)
4037
4138 Greeter *greeter = new Greeter();
42− QDBusConnection connection = QDBusConnection::sessionBus();
43− DBusGreeter *root = new DBusGreeter(greeter, connection, "/");
44− connection.registerObject("/", root, QDBusConnection::ExportScriptableContents);
45− DBusGreeterList *list = new DBusGreeterList(greeter, connection, GREETER_LIST_DBUS_PATH);
46− connection.registerObject(GREETER_LIST_DBUS_PATH, list, QDBusConnection::ExportScriptableContents);
47− connection.registerService(GREETER_DBUS_SERVICE);
39+ new DBusGreeter(greeter, "/");
40+ new DBusGreeterList(greeter, "/list");
4841
4942 return greeter;
5043}
plugins/Unity/DashCommunicator/dbusdashcommunicatorservice.cpp+1−5
@@ -21,12 +21,8 @@
2121#include <QDebug>
2222
2323DBusDashCommunicatorService::DBusDashCommunicatorService(QObject *parent):
24− QObject(parent)
24+ UnityDBusObject("/com/canonical/UnityDash", "com.canonical.UnityDash", false, parent)
2525{
26− QDBusConnection connection = QDBusConnection::sessionBus();
27−
28− connection.registerService("com.canonical.UnityDash");
29− connection.registerObject("/com/canonical/UnityDash", this, QDBusConnection::ExportScriptableSlots);
3026}
3127
3228DBusDashCommunicatorService::~DBusDashCommunicatorService()
plugins/Unity/DashCommunicator/dbusdashcommunicatorservice.h+2−2
@@ -17,9 +17,9 @@
1717#ifndef DBUSDASHCOMMUNICATORSERVICE_H
1818#define DBUSDASHCOMMUNICATORSERVICE_H
1919
20−#include <QObject>
20+#include "unitydbusobject.h"
2121
22−class DBusDashCommunicatorService: public QObject
22+class DBusDashCommunicatorService: public UnityDBusObject
2323{
2424 Q_OBJECT
2525 Q_CLASSINFO("D-Bus Interface", "com.canonical.Unity.DashCommunicator")
plugins/Unity/Launcher/CMakeLists.txt+5−1
@@ -9,6 +9,7 @@ include_directories(
99 ${CMAKE_CURRENT_SOURCE_DIR}
1010 ${CMAKE_SOURCE_DIR}/plugins/AccountsService
1111 ${GSETTINGS_QT_INCLUDE_DIRS}
12+ ${libunity8-private_SOURCE_DIR}
1213)
1314
1415set(QMLLAUNCHERPLUGIN_SRC
@@ -30,7 +31,10 @@ add_library(UnityLauncher-qml MODULE
3031 ${QMLLAUNCHERPLUGIN_SRC}
3132 )
3233
33−target_link_libraries(UnityLauncher-qml ${GSETTINGS_QT_LDFLAGS})
34+target_link_libraries(UnityLauncher-qml
35+ unity8-private
36+ ${GSETTINGS_QT_LDFLAGS}
37+ )
3438
3539qt5_use_modules(UnityLauncher-qml DBus Qml Gui)
3640
plugins/Unity/Launcher/dbusinterface.cpp+3−36
@@ -27,25 +27,13 @@
2727#include <QDebug>
2828
2929DBusInterface::DBusInterface(LauncherModel *parent):
30− QDBusVirtualObject(parent),
30+ UnityDBusVirtualObject("/com/canonical/Unity/Launcher", "com.canonical.Unity.Launcher", true, parent),
3131 m_launcherModel(parent)
3232{
33− /* Set up ourselves on DBus */
34− QDBusConnection con = QDBusConnection::sessionBus();
35− if (!con.registerService("com.canonical.Unity.Launcher")) {
36− qWarning() << "Unable to register launcher name";
37− }
38− if (!con.registerVirtualObject("/com/canonical/Unity/Launcher", this, QDBusConnection::VirtualObjectRegisterOption::SubPath)) {
39− qWarning() << "Unable to register launcher object";
40− }
4133}
4234
4335DBusInterface::~DBusInterface()
4436{
45− /* Remove oursevles from DBus */
46− QDBusConnection con = QDBusConnection::sessionBus();
47− con.unregisterService("com.canonical.Unity.Launcher");
48− con.unregisterObject("/com/canonical/Unity/Launcher");
4937}
5038
5139QString DBusInterface::introspect(const QString &path) const
@@ -186,13 +174,13 @@ bool DBusInterface::handleMessage(const QDBusMessage& message, const QDBusConnec
186174 int newCount = message.arguments()[2].toInt();
187175 if (!item || newCount != item->count()) {
188176 Q_EMIT countChanged(appid, newCount);
189− emitPropChangedDbus(appid, "count", QVariant(newCount));
177+ notifyPropertyChanged("com.canonical.Unity.Launcher.Item", encodeAppId(appid), "count", QVariant(newCount));
190178 }
191179 } else if (message.arguments()[1].toString() == "countVisible") {
192180 bool newVisible = message.arguments()[2].toBool();
193181 if (!item || newVisible != item->countVisible()) {
194182 Q_EMIT countVisibleChanged(appid, newVisible);
195− emitPropChangedDbus(appid, "countVisible", newVisible);
183+ notifyPropertyChanged("com.canonical.Unity.Launcher.Item", encodeAppId(appid), "countVisible", newVisible);
196184 }
197185 }
198186 } else if (message.member() == "GetAll") {
@@ -209,24 +197,3 @@ bool DBusInterface::handleMessage(const QDBusMessage& message, const QDBusConnec
209197 QDBusMessage reply = message.createReply(retval);
210198 return connection.send(reply);
211199}
212−
213−void DBusInterface::emitPropChangedDbus(const QString& appId, const QString& property, const QVariant &value)
214−{
215− QString path("/com/canonical/Unity/Launcher/");
216− path.append(encodeAppId(appId));
217−
218− QDBusMessage message = QDBusMessage::createSignal(path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
219−
220− QList<QVariant> arguments;
221− QVariantHash changedprops;
222− changedprops[property] = QVariant::fromValue(QDBusVariant(value));
223− QVariantList deletedprops;
224−
225− arguments.append(changedprops);
226− arguments.append(deletedprops);
227−
228− message.setArguments(arguments);
229−
230− QDBusConnection con = QDBusConnection::sessionBus();
231− con.send(message);
232−}
plugins/Unity/Launcher/dbusinterface.h+2−5
@@ -18,12 +18,11 @@
1818 */
1919
2020#include "launcheritem.h"
21−
22−#include <QDBusVirtualObject>
21+#include "unitydbusvirtualobject.h"
2322
2423class LauncherModel;
2524
26−class DBusInterface: public QDBusVirtualObject
25+class DBusInterface: public UnityDBusVirtualObject
2726{
2827 Q_OBJECT
2928public:
@@ -43,8 +42,6 @@ private:
4342 static QString decodeAppId(const QString& path);
4443 static QString encodeAppId(const QString& appId);
4544
46− void emitPropChangedDbus(const QString& appId, const QString& property, const QVariant &value);
47−
4845 LauncherModel *m_launcherModel;
4946
5047};
plugins/Unity/Session/CMakeLists.txt+5−1
@@ -5,6 +5,7 @@ pkg_search_module(GIO REQUIRED gio-2.0)
55include_directories(
66 ${CMAKE_CURRENT_SOURCE_DIR}
77 ${GIO_INCLUDE_DIRS}
8+ ${libunity8-private_SOURCE_DIR}
89)
910
1011set(QMLSESSIONPLUGIN_SRC
@@ -18,7 +19,10 @@ add_library(UnitySession-qml MODULE
1819 )
1920
2021qt5_use_modules(UnitySession-qml DBus Qml)
21−target_link_libraries(UnitySession-qml ${GIO_LDFLAGS})
22+target_link_libraries(UnitySession-qml
23+ unity8-private
24+ ${GIO_LDFLAGS}
25+ )
2226
2327# export the qmldir and qmltypes files
2428add_unity8_plugin(Unity.Session 0.1 Unity/Session TARGETS UnitySession-qml)
plugins/Unity/Session/dbusunitysessionservice.cpp+2−8
@@ -21,15 +21,9 @@
2121#include <QDBusConnection>
2222#include <QDBusInterface>
2323
24−DBusUnitySessionService::DBusUnitySessionService() : QObject()
24+DBusUnitySessionService::DBusUnitySessionService()
25+ : UnityDBusObject("/com/canonical/Unity/Session", "com.canonical.Unity")
2526{
26− QDBusConnection connection = QDBusConnection::sessionBus();
27−
28− connection.registerService("com.canonical.Unity");
29− connection.registerObject("/com/canonical/Unity/Session", this,
30− QDBusConnection::ExportScriptableSignals
31− | QDBusConnection::ExportScriptableSlots
32− | QDBusConnection::ExportScriptableInvokables);
3327}
3428
3529DBusUnitySessionService::~DBusUnitySessionService()
plugins/Unity/Session/dbusunitysessionservice.h+2−2
@@ -17,7 +17,7 @@
1717#ifndef DBUSUNITYSESSIONSERVICE_H
1818#define DBUSUNITYSESSIONSERVICE_H
1919
20−#include <QObject>
20+#include "unitydbusobject.h"
2121
2222/**
2323 * DBusUnitySessionService provides com.canonical.Unity.Session dbus
@@ -26,7 +26,7 @@
2626 * com.canonical.Unity.Session interface provides public methods
2727 * and signals to handle Logout/Reboot/Shutdown.
2828 */
29−class DBusUnitySessionService : public QObject
29+class DBusUnitySessionService : public UnityDBusObject
3030{
3131 Q_OBJECT
3232 Q_CLASSINFO("D-Bus Interface", "com.canonical.Unity.Session")
qml/Shell.qml+13
@@ -371,6 +371,10 @@ Item {
371371 onHideGreeter: greeter.login()
372372
373373 onShowPrompt: {
374+ shell.enabled = true;
375+ if (!LightDM.Greeter.active) {
376+ return; // could happen if hideGreeter() comes in before we prompt
377+ }
374378 if (greeter.narrowMode) {
375379 if (isDefaultPrompt) {
376380 if (lockscreen.alphaNumeric) {
@@ -391,6 +395,9 @@ Item {
391395 }
392396
393397 onPromptlessChanged: {
398+ if (!LightDM.Greeter.active) {
399+ return; // could happen if hideGreeter() comes in before we prompt
400+ }
394401 if (greeter.narrowMode) {
395402 if (LightDM.Greeter.promptless && LightDM.Greeter.authenticated) {
396403 lockscreen.hide()
@@ -402,6 +409,7 @@ Item {
402409 }
403410
404411 onAuthenticationComplete: {
412+ shell.enabled = true;
405413 if (LightDM.Greeter.authenticated) {
406414 AccountsService.failedLogins = 0
407415 }
@@ -538,6 +546,11 @@ Item {
538546
539547 onShownChanged: {
540548 if (shown) {
549+ // Disable everything so that user can't swipe greeter or
550+ // launcher until we get first prompt/authenticate, which
551+ // will re-enable the shell.
552+ shell.enabled = false;
553+
541554 if (greeter.narrowMode) {
542555 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
543556 } else {
src/libunity8-private/CMakeLists.txt+2
@@ -6,6 +6,8 @@ project(lib${LIB_NAME})
66
77set(lib${LIB_NAME}_SRCS
88 abstractdbusservicemonitor.cpp
9+ unitydbusobject.cpp
10+ unitydbusvirtualobject.cpp
911 )
1012
1113add_library(${LIB_NAME} SHARED
src/libunity8-private/unitydbusobject.cppnew file+86
@@ -0,0 +1,86 @@
1+/*
2+ * Copyright (C) 2014 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+
17+#include "unitydbusobject.h"
18+
19+#include <QDebug>
20+#include <QDBusMessage>
21+#include <QMetaClassInfo>
22+#include <QTimer>
23+
24+UnityDBusObject::UnityDBusObject(const QString &path, const QString &service, bool async, QObject *parent)
25+ : QObject(parent)
26+ , m_connection(QDBusConnection::sessionBus())
27+ , m_path(path)
28+ , m_service(service)
29+{
30+ if (async) {
31+ // Use a zero-timer to let Qml finish loading before we announce on DBus
32+ QTimer::singleShot(0, this, SLOT(registerObject()));
33+ } else {
34+ registerObject();
35+ }
36+}
37+
38+UnityDBusObject::~UnityDBusObject()
39+{
40+ // Leave service in place because multiple objects may be registered with
41+ // the same service. But we know we own the object path and can unregister it.
42+ m_connection.unregisterObject(path());
43+}
44+
45+QDBusConnection UnityDBusObject::connection() const
46+{
47+ return m_connection;
48+}
49+
50+QString UnityDBusObject::path() const
51+{
52+ return m_path;
53+}
54+
55+// Manually emit a PropertiesChanged signal over DBus, because QtDBus
56+// doesn't do it for us on Q_PROPERTIES, oddly enough.
57+void UnityDBusObject::notifyPropertyChanged(const QString& propertyName, const QVariant &value)
58+{
59+ QDBusMessage message;
60+ QString interface;
61+ QVariantMap changedProps;
62+
63+ interface = metaObject()->classInfo(metaObject()->indexOfClassInfo("D-Bus Interface")).value();
64+ changedProps.insert(propertyName, value);
65+
66+ message = QDBusMessage::createSignal(path(),
67+ "org.freedesktop.DBus.Properties",
68+ "PropertiesChanged");
69+ message << interface;
70+ message << changedProps;
71+ message << QStringList();
72+
73+ connection().send(message);
74+}
75+
76+void UnityDBusObject::registerObject()
77+{
78+ if (!m_connection.registerObject(m_path, this, QDBusConnection::ExportScriptableContents)) {
79+ qWarning() << "Unable to register DBus object" << m_path;
80+ }
81+ if (!m_service.isEmpty()) {
82+ if (!m_connection.registerService(m_service)) {
83+ qWarning() << "Unable to register DBus service" << m_service;
84+ }
85+ }
86+}
src/libunity8-private/unitydbusobject.hnew file+46
@@ -0,0 +1,46 @@
1+/*
2+ * Copyright (C) 2014 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+
17+#ifndef UNITYDBUSOBJECT_H
18+#define UNITYDBUSOBJECT_H
19+
20+#include <QDBusConnection>
21+#include <QObject>
22+
23+class Q_DECL_EXPORT UnityDBusObject : public QObject
24+{
25+ Q_OBJECT
26+
27+public:
28+ explicit UnityDBusObject(const QString &path, const QString &service = QString(), bool async = true, QObject *parent = 0);
29+ ~UnityDBusObject();
30+
31+ QDBusConnection connection() const;
32+ QString path() const;
33+
34+protected:
35+ void notifyPropertyChanged(const QString& propertyName, const QVariant &value);
36+
37+private Q_SLOTS:
38+ void registerObject();
39+
40+private:
41+ QDBusConnection m_connection;
42+ QString m_path;
43+ QString m_service;
44+};
45+
46+#endif // UNITYDBUSOBJECT_H
src/libunity8-private/unitydbusvirtualobject.cppnew file+83
@@ -0,0 +1,83 @@
1+/*
2+ * Copyright (C) 2014 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+
17+#include "unitydbusvirtualobject.h"
18+
19+#include <QDebug>
20+#include <QDBusMessage>
21+#include <QTimer>
22+
23+UnityDBusVirtualObject::UnityDBusVirtualObject(const QString &path, const QString &service, bool async, QObject *parent)
24+ : QDBusVirtualObject(parent)
25+ , m_connection(QDBusConnection::sessionBus())
26+ , m_path(path)
27+ , m_service(service)
28+{
29+ if (async) {
30+ // Use a zero-timer to let Qml finish loading before we announce on DBus
31+ QTimer::singleShot(0, this, SLOT(registerObject()));
32+ } else {
33+ registerObject();
34+ }
35+}
36+
37+UnityDBusVirtualObject::~UnityDBusVirtualObject()
38+{
39+ // Leave service in place because multiple objects may be registered with
40+ // the same service. But we know we own the object path and can unregister it.
41+ m_connection.unregisterObject(path());
42+}
43+
44+QDBusConnection UnityDBusVirtualObject::connection() const
45+{
46+ return m_connection;
47+}
48+
49+QString UnityDBusVirtualObject::path() const
50+{
51+ return m_path;
52+}
53+
54+// Manually emit a PropertiesChanged signal over DBus, because QtDBus
55+// doesn't do it for us on Q_PROPERTIES, oddly enough.
56+void UnityDBusVirtualObject::notifyPropertyChanged(const QString& interface, const QString& node, const QString& propertyName, const QVariant &value)
57+{
58+ QDBusMessage message;
59+ QVariantMap changedProps;
60+
61+ changedProps.insert(propertyName, value);
62+
63+ message = QDBusMessage::createSignal(path() + "/" + node,
64+ "org.freedesktop.DBus.Properties",
65+ "PropertiesChanged");
66+ message << interface;
67+ message << changedProps;
68+ message << QStringList();
69+
70+ connection().send(message);
71+}
72+
73+void UnityDBusVirtualObject::registerObject()
74+{
75+ if (!m_connection.registerVirtualObject(m_path, this, QDBusConnection::VirtualObjectRegisterOption::SubPath)) {
76+ qWarning() << "Unable to register DBus object" << m_path;
77+ }
78+ if (!m_service.isEmpty()) {
79+ if (!m_connection.registerService(m_service)) {
80+ qWarning() << "Unable to register DBus service" << m_service;
81+ }
82+ }
83+}
src/libunity8-private/unitydbusvirtualobject.hnew file+47
@@ -0,0 +1,47 @@
1+/*
2+ * Copyright (C) 2014 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+
17+#ifndef UNITYDBUSVIRTUALOBJECT_H
18+#define UNITYDBUSVIRTUALOBJECT_H
19+
20+#include <QDBusConnection>
21+#include <QDBusVirtualObject>
22+#include <QObject>
23+
24+class Q_DECL_EXPORT UnityDBusVirtualObject : public QDBusVirtualObject
25+{
26+ Q_OBJECT
27+
28+public:
29+ explicit UnityDBusVirtualObject(const QString &path, const QString &service = QString(), bool async = true, QObject *parent = 0);
30+ ~UnityDBusVirtualObject();
31+
32+ QDBusConnection connection() const;
33+ QString path() const;
34+
35+protected:
36+ void notifyPropertyChanged(const QString& interface, const QString& node, const QString& propertyName, const QVariant &value);
37+
38+private Q_SLOTS:
39+ void registerObject();
40+
41+private:
42+ QDBusConnection m_connection;
43+ QString m_path;
44+ QString m_service;
45+};
46+
47+#endif // UNITYDBUSVIRTUALOBJECT_H
tests/mocks/LightDM/CMakeLists.txt+2
@@ -15,6 +15,7 @@ include_directories(
1515 ${CMAKE_CURRENT_BINARY_DIR}
1616 ${CMAKE_SOURCE_DIR}/plugins/Utils
1717 ${CMAKE_SOURCE_DIR}/tests/mocks/libusermetrics
18+ ${libunity8-private_SOURCE_DIR}
1819)
1920
2021set(QMLPLUGIN_SRC
@@ -40,6 +41,7 @@ target_link_libraries(MockLightDM-qml
4041 -llightdm-qt5-2
4142 -L${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics
4243 -lusermetricsoutput
44+ unity8-private
4345 )
4446
4547qt5_use_modules(MockLightDM-qml DBus Gui Qml)
tests/plugins/Unity/Launcher/CMakeLists.txt+5−1
@@ -7,6 +7,7 @@ include_directories(
77 ${CMAKE_SOURCE_DIR}/plugins/AccountsService
88 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher
99 ${GSETTINGS_QT_INCLUDE_DIRS}
10+ ${libunity8-private_SOURCE_DIR}
1011 )
1112
1213add_definitions(-DSM_BUSNAME=sessionBus)
@@ -34,7 +35,10 @@ add_executable(launchermodeltestExec
3435 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
3536 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
3637 )
37−target_link_libraries(launchermodeltestExec ${GSETTINGS_QT_LDFLAGS})
38+target_link_libraries(launchermodeltestExec
39+ unity8-private
40+ ${GSETTINGS_QT_LDFLAGS}
41+ )
3842qt5_use_modules(launchermodeltestExec Test Core DBus Xml Gui)
3943
4044# copy .desktop files into build directory for shadow builds
tests/plugins/Unity/Launcher/launchermodeltest.cpp+1
@@ -133,6 +133,7 @@ private Q_SLOTS:
133133
134134 void initTestCase() {
135135 launcherModel = new LauncherModel(this);
136+ QCoreApplication::processEvents(); // to let the model register on DBus
136137 QCOMPARE(launcherModel->rowCount(QModelIndex()), 0);
137138
138139 appManager = new MockAppManager(this);
tests/plugins/Unity/Session/CMakeLists.txt+4
@@ -1,6 +1,7 @@
11include_directories(
22 ${CMAKE_CURRENT_BINARY_DIR}
33 ${CMAKE_SOURCE_DIR}/plugins/Unity/Session
4+ ${libunity8-private_SOURCE_DIR}
45)
56
67add_definitions(-DSM_BUSNAME=sessionBus)
@@ -17,4 +18,7 @@ add_executable(sessionbackendtestExec
1718 sessionbackendtest.cpp
1819 ${CMAKE_SOURCE_DIR}/plugins/Unity/Session/dbusunitysessionservice.cpp
1920)
21+target_link_libraries(sessionbackendtestExec
22+ unity8-private
23+ )
2024qt5_use_modules(sessionbackendtestExec Test Core Qml DBus)
tests/plugins/Unity/Session/sessionbackendtest.cpp+1
@@ -45,6 +45,7 @@ private Q_SLOTS:
4545 QFETCH(QString, method);
4646
4747 DBusUnitySessionService dbusUnitySessionService;
48+ QCoreApplication::processEvents(); // to let the service register on DBus
4849
4950 QDBusConnection con = QDBusConnection::sessionBus();
5051 QDBusInterface interface ("com.canonical.Unity",
tests/qmltests/tst_Shell.qml+3
@@ -161,6 +161,8 @@ Item {
161161 property Item shell: shellLoader.status === Loader.Ready ? shellLoader.item : null
162162
163163 function init() {
164+ tryCompare(shell, "enabled", true); // enabled by greeter when ready
165+
164166 swipeAwayGreeter();
165167
166168 sessionSpy.target = findChild(shell, "greeter")
@@ -171,6 +173,7 @@ Item {
171173 }
172174
173175 function cleanup() {
176+ tryCompare(shell, "enabled", true); // make sure greeter didn't leave us in disabled state
174177 launcherShowDashHomeSpy.target = null;
175178
176179 shellLoader.itemDestroyed = false;
tests/qmltests/tst_ShellWithPin.qml+3
@@ -129,6 +129,7 @@ Item {
129129 property Item shell: shellLoader.status === Loader.Ready ? shellLoader.item : null
130130
131131 function init() {
132+ tryCompare(shell, "enabled", true); // will be enabled when greeter is all ready
132133 sessionSpy.target = findChild(shell, "greeter")
133134 swipeAwayGreeter()
134135 waitForLockscreen()
@@ -137,6 +138,8 @@ Item {
137138 }
138139
139140 function cleanup() {
141+ tryCompare(shell, "enabled", true); // make sure greeter didn't leave us in disabled state
142+
140143 shellLoader.itemDestroyed = false
141144
142145 shellLoader.active = false
tests/qmltests/tst_TabletShell.qml+3
@@ -121,12 +121,15 @@ Row {
121121 property Item shell: shellLoader.status === Loader.Ready ? shellLoader.item : null
122122
123123 function init() {
124+ tryCompare(shell, "enabled", true); // will be enabled when greeter is all ready
124125 sessionSpy.clear()
125126 sessionSpy.target = findChild(shell, "greeter")
126127 dashCommunicatorSpy.target = findInvisibleChild(shell, "dashCommunicator")
127128 }
128129
129130 function cleanup() {
131+ tryCompare(shell, "enabled", true); // make sure greeter didn't leave us in disabled state
132+
130133 shellLoader.itemDestroyed = false
131134
132135 shellLoader.active = false