1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2016 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include "qdbusabstractadaptor.h"
6#include "qdbusabstractadaptor_p.h"
7
8#include <QtCore/qcoreapplication.h>
9#include <QtCore/qmetaobject.h>
10#include <QtCore/qset.h>
11#include <QtCore/qtimer.h>
12#include <QtCore/qthread.h>
13
14#include "qdbusconnection.h"
15
16#include "qdbusconnection_p.h" // for qDBusParametersForMethod
17#include "qdbusmetatype_p.h"
18
19#include <algorithm>
20
21#ifndef QT_NO_DBUS
22
23QT_BEGIN_NAMESPACE
24
25static int cachedRelaySlotMethodIndex = 0;
26
27int QDBusAdaptorConnector::relaySlotMethodIndex()
28{
29 if (cachedRelaySlotMethodIndex == 0) {
30 cachedRelaySlotMethodIndex = staticMetaObject.indexOfMethod(method: "relaySlot()");
31 Q_ASSERT(cachedRelaySlotMethodIndex != 0); // 0 should be deleteLater() or destroyed()
32 }
33 return cachedRelaySlotMethodIndex;
34}
35
36QDBusAdaptorConnector *qDBusFindAdaptorConnector(QObject *obj)
37{
38 if (!obj)
39 return nullptr;
40
41 for (QObject *child : std::as_const(t: obj->children())) {
42 QDBusAdaptorConnector *connector = qobject_cast<QDBusAdaptorConnector *>(object: child);
43 if (connector) {
44 connector->polish();
45 return connector;
46 }
47 }
48 return nullptr;
49}
50
51QDBusAdaptorConnector *qDBusFindAdaptorConnector(QDBusAbstractAdaptor *adaptor)
52{
53 return qDBusFindAdaptorConnector(obj: adaptor->parent());
54}
55
56QDBusAdaptorConnector *qDBusCreateAdaptorConnector(QObject *obj)
57{
58 QDBusAdaptorConnector *connector = qDBusFindAdaptorConnector(obj);
59 if (connector)
60 return connector;
61 return new QDBusAdaptorConnector(obj);
62}
63
64QString QDBusAbstractAdaptorPrivate::retrieveIntrospectionXml(QDBusAbstractAdaptor *adaptor)
65{
66 return adaptor->d_func()->xml;
67}
68
69void QDBusAbstractAdaptorPrivate::saveIntrospectionXml(QDBusAbstractAdaptor *adaptor,
70 const QString &xml)
71{
72 adaptor->d_func()->xml = xml;
73}
74
75/*!
76 \class QDBusAbstractAdaptor
77 \inmodule QtDBus
78 \since 4.2
79
80 \brief The QDBusAbstractAdaptor class is the base class of D-Bus adaptor classes.
81
82 The QDBusAbstractAdaptor class is the starting point for all objects intending to provide
83 interfaces to the external world using D-Bus. This is accomplished by attaching a one or more
84 classes derived from QDBusAbstractAdaptor to a normal QObject and then registering that QObject
85 with QDBusConnection::registerObject. QDBusAbstractAdaptor objects are intended to be
86 light-weight wrappers, mostly just relaying calls into the real object (its parent) and the
87 signals from it.
88
89 Each QDBusAbstractAdaptor-derived class should define the D-Bus interface it is implementing
90 using the Q_CLASSINFO macro in the class definition. Note that only one interface can be
91 exposed in this way.
92
93 QDBusAbstractAdaptor uses the standard QObject mechanism of signals, slots and properties to
94 determine what signals, methods and properties to export to the bus. Any signal emitted by
95 QDBusAbstractAdaptor-derived classes will be automatically be relayed through any D-Bus
96 connections the object is registered on.
97
98 Classes derived from QDBusAbstractAdaptor must be created on the heap using the \a new operator
99 and must not be deleted by the user (they will be deleted automatically when the object they are
100 connected to is also deleted).
101
102 \sa {usingadaptors.html}{Using adaptors}, QDBusConnection
103*/
104
105/*!
106 Constructs a QDBusAbstractAdaptor with \a obj as the parent object.
107*/
108QDBusAbstractAdaptor::QDBusAbstractAdaptor(QObject* obj)
109 : QObject(*new QDBusAbstractAdaptorPrivate, obj)
110{
111 QDBusAdaptorConnector *connector = qDBusCreateAdaptorConnector(obj);
112
113 connector->waitingForPolish = true;
114 QMetaObject::invokeMethod(object: connector, function: &QDBusAdaptorConnector::polish, type: Qt::QueuedConnection);
115}
116
117/*!
118 Destroys the adaptor.
119
120 \warning Adaptors are destroyed automatically when the real object they refer to is
121 destroyed. Do not delete the adaptors yourself.
122*/
123QDBusAbstractAdaptor::~QDBusAbstractAdaptor()
124{
125}
126
127/*!
128 Toggles automatic signal relaying from the real object (see object()).
129
130 Automatic signal relaying consists of signal-to-signal connection of the signals on the parent
131 that have the exact same method signature in both classes.
132
133 If \a enable is set to true, connect the signals; if set to false, disconnect all signals.
134*/
135void QDBusAbstractAdaptor::setAutoRelaySignals(bool enable)
136{
137 const QMetaObject *us = metaObject();
138 const QMetaObject *them = parent()->metaObject();
139 bool connected = false;
140 for (int idx = staticMetaObject.methodCount(); idx < us->methodCount(); ++idx) {
141 QMetaMethod mm = us->method(index: idx);
142
143 if (mm.methodType() != QMetaMethod::Signal)
144 continue;
145
146 // try to connect/disconnect to a signal on the parent that has the same method signature
147 QByteArray sig = QMetaObject::normalizedSignature(method: mm.methodSignature().constData());
148 if (them->indexOfSignal(signal: sig) == -1)
149 continue;
150 sig.prepend(QSIGNAL_CODE + '0');
151 parent()->disconnect(signal: sig, receiver: this, member: sig);
152 if (enable)
153 connected = connect(asender: parent(), asignal: sig, amember: sig) || connected;
154 }
155 d_func()->autoRelaySignals = connected;
156}
157
158/*!
159 Returns \c true if automatic signal relaying from the real object (see object()) is enabled,
160 otherwiser returns \c false.
161
162 \sa setAutoRelaySignals()
163*/
164bool QDBusAbstractAdaptor::autoRelaySignals() const
165{
166 return d_func()->autoRelaySignals;
167}
168
169QDBusAdaptorConnector::QDBusAdaptorConnector(QObject *obj)
170 : QObject(obj), waitingForPolish(false)
171{
172}
173
174QDBusAdaptorConnector::~QDBusAdaptorConnector()
175{
176}
177
178void QDBusAdaptorConnector::addAdaptor(QDBusAbstractAdaptor *adaptor)
179{
180 // find the interface name
181 const QMetaObject *mo = adaptor->metaObject();
182 int ciid = mo->indexOfClassInfo(QCLASSINFO_DBUS_INTERFACE);
183 if (ciid != -1) {
184 QMetaClassInfo mci = mo->classInfo(index: ciid);
185 if (*mci.value()) {
186 // find out if this interface exists first
187 const char *interface = mci.value();
188 AdaptorMap::Iterator it = std::lower_bound(first: adaptors.begin(), last: adaptors.end(),
189 val: QByteArray(interface));
190 if (it != adaptors.end() && qstrcmp(str1: interface, str2: it->interface) == 0) {
191 // exists. Replace it (though it's probably the same)
192 if (it->adaptor != adaptor) {
193 // reconnect the signals
194 disconnectAllSignals(object: it->adaptor);
195 connectAllSignals(object: adaptor);
196 }
197 it->adaptor = adaptor;
198 } else {
199 // create a new one
200 AdaptorData entry;
201 entry.interface = interface;
202 entry.adaptor = adaptor;
203 adaptors << entry;
204
205 // connect the adaptor's signals to our relaySlot slot
206 connectAllSignals(object: adaptor);
207 }
208 }
209 }
210}
211
212void QDBusAdaptorConnector::disconnectAllSignals(QObject *obj)
213{
214 QMetaObject::disconnect(sender: obj, signal_index: -1, receiver: this, method_index: relaySlotMethodIndex());
215}
216
217void QDBusAdaptorConnector::connectAllSignals(QObject *obj)
218{
219 QMetaObject::connect(sender: obj, signal_index: -1, receiver: this, method_index: relaySlotMethodIndex(), type: Qt::DirectConnection);
220}
221
222void QDBusAdaptorConnector::polish()
223{
224 if (!waitingForPolish)
225 return; // avoid working multiple times if multiple adaptors were added
226
227 waitingForPolish = false;
228 for (QObject *child : std::as_const(t: parent()->children())) {
229 QDBusAbstractAdaptor *adaptor = qobject_cast<QDBusAbstractAdaptor *>(object: child);
230 if (adaptor)
231 addAdaptor(adaptor);
232 }
233
234 // sort the adaptor list
235 std::sort(first: adaptors.begin(), last: adaptors.end());
236}
237
238void QDBusAdaptorConnector::relaySlot(QMethodRawArguments argv)
239{
240 QObject *sndr = sender();
241 if (Q_LIKELY(sndr)) {
242 relay(sender: sndr, id: senderSignalIndex(), argv.arguments);
243 } else {
244 qWarning(msg: "QtDBus: cannot relay signals from parent %s(%p \"%s\") unless they are emitted in the object's thread %s(%p \"%s\"). "
245 "Current thread is %s(%p \"%s\").",
246 parent()->metaObject()->className(), parent(), qPrintable(parent()->objectName()),
247 parent()->thread()->metaObject()->className(), parent()->thread(), qPrintable(parent()->thread()->objectName()),
248 QThread::currentThread()->metaObject()->className(), QThread::currentThread(), qPrintable(QThread::currentThread()->objectName()));
249 }
250}
251
252void QDBusAdaptorConnector::relay(QObject *senderObj, int lastSignalIdx, void **argv)
253{
254 if (lastSignalIdx < QObject::staticMetaObject.methodCount())
255 // QObject signal (destroyed(QObject *)) -- ignore
256 return;
257
258 QMetaMethod mm = senderObj->metaObject()->method(index: lastSignalIdx);
259 const QMetaObject *senderMetaObject = mm.enclosingMetaObject();
260
261 QObject *realObject = senderObj;
262 if (qobject_cast<QDBusAbstractAdaptor *>(object: senderObj))
263 // it's an adaptor, so the real object is in fact its parent
264 realObject = realObject->parent();
265
266 // break down the parameter list
267 QList<QMetaType> types;
268 QString errorMsg;
269 int inputCount = qDBusParametersForMethod(mm, metaTypes&: types, errorMsg);
270 if (inputCount == -1) {
271 // invalid signal signature
272 qWarning(msg: "QDBusAbstractAdaptor: Cannot relay signal %s::%s: %s",
273 senderMetaObject->className(), mm.methodSignature().constData(),
274 qPrintable(errorMsg));
275 return;
276 }
277 if (inputCount + 1 != types.size() ||
278 types.at(i: inputCount) == QDBusMetaTypeId::message()) {
279 // invalid signal signature
280 qWarning(msg: "QDBusAbstractAdaptor: Cannot relay signal %s::%s",
281 senderMetaObject->className(), mm.methodSignature().constData());
282 return;
283 }
284
285 QVariantList args;
286 const int numTypes = types.size();
287 args.reserve(asize: numTypes - 1);
288 for (int i = 1; i < numTypes; ++i)
289 args << QVariant(QMetaType(types.at(i)), argv[i]);
290
291 // now emit the signal with all the information
292 emit relaySignal(obj: realObject, metaObject: senderMetaObject, sid: lastSignalIdx, args);
293}
294
295QT_END_NAMESPACE
296
297#include "moc_qdbusabstractadaptor_p.cpp"
298#include "moc_qdbusabstractadaptor.cpp"
299
300#endif // QT_NO_DBUS
301

source code of qtbase/src/dbus/qdbusabstractadaptor.cpp