1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Copyright (C) 2016 Intel Corporation.
5** Contact: https://www.qt.io/licensing/
6**
7** This file is part of the QtDBus module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial License Usage
11** Licensees holding valid commercial Qt licenses may use this file in
12** accordance with the commercial license agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and The Qt Company. For licensing terms
15** and conditions see https://www.qt.io/terms-conditions. For further
16** information use the contact form at https://www.qt.io/contact-us.
17**
18** GNU Lesser General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU Lesser
20** General Public License version 3 as published by the Free Software
21** Foundation and appearing in the file LICENSE.LGPL3 included in the
22** packaging of this file. Please review the following information to
23** ensure the GNU Lesser General Public License version 3 requirements
24** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25**
26** GNU General Public License Usage
27** Alternatively, this file may be used under the terms of the GNU
28** General Public License version 2.0 or (at your option) the GNU General
29** Public license version 3 or any later version approved by the KDE Free
30** Qt Foundation. The licenses are as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
32** included in the packaging of this file. Please review the following
33** information to ensure the GNU General Public License requirements will
34** be met: https://www.gnu.org/licenses/gpl-2.0.html and
35** https://www.gnu.org/licenses/gpl-3.0.html.
36**
37** $QT_END_LICENSE$
38**
39****************************************************************************/
40
41#include "qdbusabstractadaptor.h"
42#include "qdbusabstractadaptor_p.h"
43
44#include <QtCore/qcoreapplication.h>
45#include <QtCore/qmetaobject.h>
46#include <QtCore/qset.h>
47#include <QtCore/qtimer.h>
48#include <QtCore/qthread.h>
49
50#include "qdbusconnection.h"
51
52#include "qdbusconnection_p.h" // for qDBusParametersForMethod
53#include "qdbusmetatype_p.h"
54
55#include <algorithm>
56
57#ifndef QT_NO_DBUS
58
59QT_BEGIN_NAMESPACE
60
61static int cachedRelaySlotMethodIndex = -1;
62
63int QDBusAdaptorConnector::relaySlotMethodIndex()
64{
65 if (cachedRelaySlotMethodIndex == -1) {
66 cachedRelaySlotMethodIndex = staticMetaObject.indexOfMethod("relaySlot()");
67 Q_ASSERT(cachedRelaySlotMethodIndex != -1);
68 }
69 return cachedRelaySlotMethodIndex;
70}
71
72QDBusAdaptorConnector *qDBusFindAdaptorConnector(QObject *obj)
73{
74 if (!obj)
75 return 0;
76 const QObjectList &children = obj->children();
77 QObjectList::ConstIterator it = children.constBegin();
78 QObjectList::ConstIterator end = children.constEnd();
79 for ( ; it != end; ++it) {
80 QDBusAdaptorConnector *connector = qobject_cast<QDBusAdaptorConnector *>(*it);
81 if (connector) {
82 connector->polish();
83 return connector;
84 }
85 }
86 return 0;
87}
88
89QDBusAdaptorConnector *qDBusFindAdaptorConnector(QDBusAbstractAdaptor *adaptor)
90{
91 return qDBusFindAdaptorConnector(adaptor->parent());
92}
93
94QDBusAdaptorConnector *qDBusCreateAdaptorConnector(QObject *obj)
95{
96 QDBusAdaptorConnector *connector = qDBusFindAdaptorConnector(obj);
97 if (connector)
98 return connector;
99 return new QDBusAdaptorConnector(obj);
100}
101
102QString QDBusAbstractAdaptorPrivate::retrieveIntrospectionXml(QDBusAbstractAdaptor *adaptor)
103{
104 return adaptor->d_func()->xml;
105}
106
107void QDBusAbstractAdaptorPrivate::saveIntrospectionXml(QDBusAbstractAdaptor *adaptor,
108 const QString &xml)
109{
110 adaptor->d_func()->xml = xml;
111}
112
113/*!
114 \class QDBusAbstractAdaptor
115 \inmodule QtDBus
116 \since 4.2
117
118 \brief The QDBusAbstractAdaptor class is the base class of D-Bus adaptor classes.
119
120 The QDBusAbstractAdaptor class is the starting point for all objects intending to provide
121 interfaces to the external world using D-Bus. This is accomplished by attaching a one or more
122 classes derived from QDBusAbstractAdaptor to a normal QObject and then registering that QObject
123 with QDBusConnection::registerObject. QDBusAbstractAdaptor objects are intended to be
124 light-weight wrappers, mostly just relaying calls into the real object (its parent) and the
125 signals from it.
126
127 Each QDBusAbstractAdaptor-derived class should define the D-Bus interface it is implementing
128 using the Q_CLASSINFO macro in the class definition. Note that only one interface can be
129 exposed in this way.
130
131 QDBusAbstractAdaptor uses the standard QObject mechanism of signals, slots and properties to
132 determine what signals, methods and properties to export to the bus. Any signal emitted by
133 QDBusAbstractAdaptor-derived classes will be automatically be relayed through any D-Bus
134 connections the object is registered on.
135
136 Classes derived from QDBusAbstractAdaptor must be created on the heap using the \a new operator
137 and must not be deleted by the user (they will be deleted automatically when the object they are
138 connected to is also deleted).
139
140 \sa {usingadaptors.html}{Using adaptors}, QDBusConnection
141*/
142
143/*!
144 Constructs a QDBusAbstractAdaptor with \a obj as the parent object.
145*/
146QDBusAbstractAdaptor::QDBusAbstractAdaptor(QObject* obj)
147 : QObject(*new QDBusAbstractAdaptorPrivate, obj)
148{
149 QDBusAdaptorConnector *connector = qDBusCreateAdaptorConnector(obj);
150
151 connector->waitingForPolish = true;
152 QMetaObject::invokeMethod(connector, "polish", Qt::QueuedConnection);
153}
154
155/*!
156 Destroys the adaptor.
157
158 \warning Adaptors are destroyed automatically when the real object they refer to is
159 destroyed. Do not delete the adaptors yourself.
160*/
161QDBusAbstractAdaptor::~QDBusAbstractAdaptor()
162{
163}
164
165/*!
166 Toggles automatic signal relaying from the real object (see object()).
167
168 Automatic signal relaying consists of signal-to-signal connection of the signals on the parent
169 that have the exact same method signatue in both classes.
170
171 If \a enable is set to true, connect the signals; if set to false, disconnect all signals.
172*/
173void QDBusAbstractAdaptor::setAutoRelaySignals(bool enable)
174{
175 const QMetaObject *us = metaObject();
176 const QMetaObject *them = parent()->metaObject();
177 bool connected = false;
178 for (int idx = staticMetaObject.methodCount(); idx < us->methodCount(); ++idx) {
179 QMetaMethod mm = us->method(idx);
180
181 if (mm.methodType() != QMetaMethod::Signal)
182 continue;
183
184 // try to connect/disconnect to a signal on the parent that has the same method signature
185 QByteArray sig = QMetaObject::normalizedSignature(mm.methodSignature().constData());
186 if (them->indexOfSignal(sig) == -1)
187 continue;
188 sig.prepend(QSIGNAL_CODE + '0');
189 parent()->disconnect(sig, this, sig);
190 if (enable)
191 connected = connect(parent(), sig, sig) || connected;
192 }
193 d_func()->autoRelaySignals = connected;
194}
195
196/*!
197 Returns \c true if automatic signal relaying from the real object (see object()) is enabled,
198 otherwiser returns \c false.
199
200 \sa setAutoRelaySignals()
201*/
202bool QDBusAbstractAdaptor::autoRelaySignals() const
203{
204 return d_func()->autoRelaySignals;
205}
206
207QDBusAdaptorConnector::QDBusAdaptorConnector(QObject *obj)
208 : QObject(obj), waitingForPolish(false)
209{
210}
211
212QDBusAdaptorConnector::~QDBusAdaptorConnector()
213{
214}
215
216void QDBusAdaptorConnector::addAdaptor(QDBusAbstractAdaptor *adaptor)
217{
218 // find the interface name
219 const QMetaObject *mo = adaptor->metaObject();
220 int ciid = mo->indexOfClassInfo(QCLASSINFO_DBUS_INTERFACE);
221 if (ciid != -1) {
222 QMetaClassInfo mci = mo->classInfo(ciid);
223 if (*mci.value()) {
224 // find out if this interface exists first
225 const char *interface = mci.value();
226 AdaptorMap::Iterator it = std::lower_bound(adaptors.begin(), adaptors.end(),
227 QByteArray(interface));
228 if (it != adaptors.end() && qstrcmp(interface, it->interface) == 0) {
229 // exists. Replace it (though it's probably the same)
230 if (it->adaptor != adaptor) {
231 // reconnect the signals
232 disconnectAllSignals(it->adaptor);
233 connectAllSignals(adaptor);
234 }
235 it->adaptor = adaptor;
236 } else {
237 // create a new one
238 AdaptorData entry;
239 entry.interface = interface;
240 entry.adaptor = adaptor;
241 adaptors << entry;
242
243 // connect the adaptor's signals to our relaySlot slot
244 connectAllSignals(adaptor);
245 }
246 }
247 }
248}
249
250void QDBusAdaptorConnector::disconnectAllSignals(QObject *obj)
251{
252 QMetaObject::disconnect(obj, -1, this, relaySlotMethodIndex());
253}
254
255void QDBusAdaptorConnector::connectAllSignals(QObject *obj)
256{
257 QMetaObject::connect(obj, -1, this, relaySlotMethodIndex(), Qt::DirectConnection);
258}
259
260void QDBusAdaptorConnector::polish()
261{
262 if (!waitingForPolish)
263 return; // avoid working multiple times if multiple adaptors were added
264
265 waitingForPolish = false;
266 const QObjectList &objs = parent()->children();
267 QObjectList::ConstIterator it = objs.constBegin();
268 QObjectList::ConstIterator end = objs.constEnd();
269 for ( ; it != end; ++it) {
270 QDBusAbstractAdaptor *adaptor = qobject_cast<QDBusAbstractAdaptor *>(*it);
271 if (adaptor)
272 addAdaptor(adaptor);
273 }
274
275 // sort the adaptor list
276 std::sort(adaptors.begin(), adaptors.end());
277}
278
279void QDBusAdaptorConnector::relaySlot(void **argv)
280{
281 QObject *sndr = sender();
282 if (Q_LIKELY(sndr)) {
283 relay(sndr, senderSignalIndex(), argv);
284 } else {
285 qWarning("QtDBus: cannot relay signals from parent %s(%p \"%s\") unless they are emitted in the object's thread %s(%p \"%s\"). "
286 "Current thread is %s(%p \"%s\").",
287 parent()->metaObject()->className(), parent(), qPrintable(parent()->objectName()),
288 parent()->thread()->metaObject()->className(), parent()->thread(), qPrintable(parent()->thread()->objectName()),
289 QThread::currentThread()->metaObject()->className(), QThread::currentThread(), qPrintable(QThread::currentThread()->objectName()));
290 }
291}
292
293void QDBusAdaptorConnector::relay(QObject *senderObj, int lastSignalIdx, void **argv)
294{
295 if (lastSignalIdx < QObject::staticMetaObject.methodCount())
296 // QObject signal (destroyed(QObject *)) -- ignore
297 return;
298
299 const QMetaObject *senderMetaObject = senderObj->metaObject();
300 QMetaMethod mm = senderMetaObject->method(lastSignalIdx);
301
302 QObject *realObject = senderObj;
303 if (qobject_cast<QDBusAbstractAdaptor *>(senderObj))
304 // it's an adaptor, so the real object is in fact its parent
305 realObject = realObject->parent();
306
307 // break down the parameter list
308 QVector<int> types;
309 QString errorMsg;
310 int inputCount = qDBusParametersForMethod(mm, types, errorMsg);
311 if (inputCount == -1) {
312 // invalid signal signature
313 qWarning("QDBusAbstractAdaptor: Cannot relay signal %s::%s: %s",
314 senderMetaObject->className(), mm.methodSignature().constData(),
315 qPrintable(errorMsg));
316 return;
317 }
318 if (inputCount + 1 != types.count() ||
319 types.at(inputCount) == QDBusMetaTypeId::message()) {
320 // invalid signal signature
321 qWarning("QDBusAbstractAdaptor: Cannot relay signal %s::%s",
322 senderMetaObject->className(), mm.methodSignature().constData());
323 return;
324 }
325
326 QVariantList args;
327 const int numTypes = types.count();
328 args.reserve(numTypes - 1);
329 for (int i = 1; i < numTypes; ++i)
330 args << QVariant(types.at(i), argv[i]);
331
332 // now emit the signal with all the information
333 emit relaySignal(realObject, senderMetaObject, lastSignalIdx, args);
334}
335
336// our Meta Object
337// modify carefully: this has been hand-edited!
338// the relaySlot slot gets called with the void** array
339
340struct qt_meta_stringdata_QDBusAdaptorConnector_t {
341 QByteArrayData data[10];
342 char stringdata[96];
343};
344#define QT_MOC_LITERAL(idx, ofs, len) \
345 Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \
346 offsetof(qt_meta_stringdata_QDBusAdaptorConnector_t, stringdata) + ofs \
347 - idx * sizeof(QByteArrayData) \
348 )
349static const qt_meta_stringdata_QDBusAdaptorConnector_t qt_meta_stringdata_QDBusAdaptorConnector = {
350 {
351QT_MOC_LITERAL(0, 0, 21),
352QT_MOC_LITERAL(1, 22, 11),
353QT_MOC_LITERAL(2, 34, 0),
354QT_MOC_LITERAL(3, 35, 3),
355QT_MOC_LITERAL(4, 39, 18),
356QT_MOC_LITERAL(5, 58, 10),
357QT_MOC_LITERAL(6, 69, 3),
358QT_MOC_LITERAL(7, 73, 4),
359QT_MOC_LITERAL(8, 78, 9),
360QT_MOC_LITERAL(9, 88, 6)
361 },
362 "QDBusAdaptorConnector\0relaySignal\0\0"
363 "obj\0const QMetaObject*\0metaObject\0sid\0"
364 "args\0relaySlot\0polish\0"
365};
366#undef QT_MOC_LITERAL
367
368static const uint qt_meta_data_QDBusAdaptorConnector[] = {
369
370 // content:
371 7, // revision
372 0, // classname
373 0, 0, // classinfo
374 3, 14, // methods
375 0, 0, // properties
376 0, 0, // enums/sets
377 0, 0, // constructors
378 0, // flags
379 1, // signalCount
380
381 // signals: name, argc, parameters, tag, flags
382 1, 4, 29, 2, 0x05,
383
384 // slots: name, argc, parameters, tag, flags
385 8, 0, 38, 2, 0x0a,
386 9, 0, 39, 2, 0x0a,
387
388 // signals: parameters
389 QMetaType::Void, QMetaType::QObjectStar, 0x80000000 | 4, QMetaType::Int, QMetaType::QVariantList, 3, 5, 6, 7,
390
391 // slots: parameters
392 QMetaType::Void,
393 QMetaType::Void,
394
395 0 // eod
396};
397
398void QDBusAdaptorConnector::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
399{
400 if (_c == QMetaObject::InvokeMetaMethod) {
401 Q_ASSERT(staticMetaObject.cast(_o));
402 QDBusAdaptorConnector *_t = static_cast<QDBusAdaptorConnector *>(_o);
403 switch (_id) {
404 case 0: _t->relaySignal((*reinterpret_cast< QObject*(*)>(_a[1])),(*reinterpret_cast< const QMetaObject*(*)>(_a[2])),(*reinterpret_cast< int(*)>(_a[3])),(*reinterpret_cast< const QVariantList(*)>(_a[4]))); break;
405 case 1: _t->relaySlot(_a); break; // HAND EDIT: add the _a parameter
406 case 2: _t->polish(); break;
407 default: ;
408 }
409 }
410}
411
412const QMetaObject QDBusAdaptorConnector::staticMetaObject = {
413 { &QObject::staticMetaObject, qt_meta_stringdata_QDBusAdaptorConnector.data,
414 qt_meta_data_QDBusAdaptorConnector, qt_static_metacall, 0, 0 }
415};
416
417const QMetaObject *QDBusAdaptorConnector::metaObject() const
418{
419 return &staticMetaObject;
420}
421
422void *QDBusAdaptorConnector::qt_metacast(const char *_clname)
423{
424 if (!_clname) return 0;
425 if (!strcmp(_clname, qt_meta_stringdata_QDBusAdaptorConnector.stringdata))
426 return static_cast<void*>(const_cast< QDBusAdaptorConnector*>(this));
427 return QObject::qt_metacast(_clname);
428}
429
430int QDBusAdaptorConnector::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
431{
432 _id = QObject::qt_metacall(_c, _id, _a);
433 if (_id < 0)
434 return _id;
435 if (_c == QMetaObject::InvokeMetaMethod) {
436 if (_id < 3)
437 qt_static_metacall(this, _c, _id, _a);
438 _id -= 3;
439 }
440 return _id;
441}
442
443// SIGNAL 0
444void QDBusAdaptorConnector::relaySignal(QObject * _t1, const QMetaObject * _t2, int _t3, const QVariantList & _t4)
445{
446 void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)), const_cast<void*>(reinterpret_cast<const void*>(&_t2)), const_cast<void*>(reinterpret_cast<const void*>(&_t3)), const_cast<void*>(reinterpret_cast<const void*>(&_t4)) };
447 QMetaObject::activate(this, &staticMetaObject, 0, _a);
448}
449
450QT_END_NAMESPACE
451
452#endif // QT_NO_DBUS
453