1/****************************************************************************
2**
3** Copyright (C) 2015 The Qt Company Ltd and/or its subsidiary(-ies).
4** Contact: http://www.qt-project.org/legal
5**
6** This file is part of the QtSystems module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL21$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see http://www.qt.io/terms-conditions. For further
15** information use the contact form at http://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 2.1 or version 3 as published by the Free
20** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22** following information to ensure the GNU Lesser General Public License
23** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25**
26** As a special exception, The Qt Company gives you certain additional
27** rights. These rights are described in The Qt Company LGPL Exception
28** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29**
30** $QT_END_LICENSE$
31**
32****************************************************************************/
33
34#include "qservicemetaobject_dbus_p.h"
35#include <private/qmetaobjectbuilder_p.h>
36#include "qsignalintercepter_p.h"
37#include <QDebug>
38
39#include <stdlib.h>
40
41QT_BEGIN_NAMESPACE
42
43class ServiceMetaSignalIntercepter : public QSignalIntercepter
44{
45
46public:
47 ServiceMetaSignalIntercepter(QObject* sender, const QByteArray& signal, QServiceMetaObjectDBus* parent)
48 : QSignalIntercepter(sender, signal, parent), serviceDBus(parent)
49 {
50
51 }
52
53 void setMetaIndex(int index)
54 {
55 metaIndex = index;
56 }
57
58protected:
59 void activated( const QList<QVariant>& args )
60 {
61 serviceDBus->activateMetaSignal(id: metaIndex, args);
62 }
63private:
64 QServiceMetaObjectDBus* serviceDBus;
65 int metaIndex;
66};
67
68
69class QServiceMetaObjectDBusPrivate
70{
71public:
72 QObject *service;
73 const QMetaObject *serviceMeta;
74 const QMetaObject *dbusMeta;
75};
76
77QServiceMetaObjectDBus::QServiceMetaObjectDBus(QObject* service, bool signalsObject)
78 : QDBusAbstractAdaptor(service)
79{
80 // Register our DBus custom type object
81 qRegisterMetaType<QServiceUserTypeDBus>();
82 qDBusRegisterMetaType<QServiceUserTypeDBus>();
83
84 // Generate our DBus meta object
85 d = new QServiceMetaObjectDBusPrivate();
86 d->service = service;
87 d->serviceMeta = service->metaObject();
88 d->dbusMeta = dbusMetaObject(signalsObject);
89
90 // Relay signals from the service to the constructed DBus meta object
91 connectMetaSignals(signalsObject);
92}
93
94QServiceMetaObjectDBus::~QServiceMetaObjectDBus()
95{
96 if (d->dbusMeta)
97 free(ptr: const_cast<QMetaObject*>(d->dbusMeta));
98
99 delete d;
100}
101
102/*!
103 Relays all signals from the service object to the converted DBus service adaptor so
104 that when a service signal is emitted the DBus object will emit the counterpart signal
105*/
106void QServiceMetaObjectDBus::connectMetaSignals(bool signalsObject) {
107 if (!signalsObject) {
108 // Automatically relay signals from service object to adaptor
109 setAutoRelaySignals(true);
110
111 // Connect signals with custom arguments
112 int methodCount = d->serviceMeta->methodCount();
113 for (int i = 0; i < methodCount; i++) {
114 QMetaMethod mm = d->serviceMeta->method(index: i);
115
116
117 if (mm.methodType() == QMetaMethod::Signal) {
118 QByteArray sig(mm.methodSignature());
119 bool customType = false;
120 const QList<QByteArray> pTypes = mm.parameterTypes();
121 const int pTypesCount = pTypes.count();
122
123 // Ignore all QObject calls
124 const QMetaObject *mo = QObject::metaObject();
125 int qobjectIndex = mo->indexOfMethod(method: sig);
126 if (qobjectIndex >= 0)
127 continue;
128
129 // Detects custom types as passed arguments
130 for (int arg = 0; arg < pTypesCount; arg++) {
131 const QByteArray& type = pTypes[arg];
132 int variantType = QMetaType::type(typeName: type);
133 if (variantType >= QMetaType::User || variantType == QMetaType::QVariant) {
134 sig.replace(before: QByteArray(type), after: QByteArray("QDBusVariant"));
135 customType = true;
136 }
137 }
138
139 // Connects the service signal to the corresponding DBus service signal
140 if (customType) {
141 QByteArray signal = mm.methodSignature();
142 ServiceMetaSignalIntercepter *intercept =
143 new ServiceMetaSignalIntercepter(d->service, "2"+signal, this);
144 intercept->setMetaIndex(i);
145 }
146 }
147 }
148 }
149}
150
151/*!
152 Relays the activation to the DBus object signal with the given id and arguments list when the
153 intercepter for signals with custom arguments has been activated. This bypasses the metacall
154 which usually does the relaying for signals with standard arguments since no pre-connection
155 conversions are required.
156*/
157void QServiceMetaObjectDBus::activateMetaSignal(int id, const QVariantList& args)
158{
159 QMetaMethod method = d->serviceMeta->method(index: id);
160
161 // Converts the argument list to values supported by the QtDBus type system
162 QVariantList convertedList = args;
163 QByteArray sig(method.methodSignature());
164 QList<QByteArray> params = method.parameterTypes();
165
166 for (int i = 0; i < params.size(); i++) {
167 QVariant dbusVariant = args[i];
168
169 // Convert custom types
170 const QByteArray& type = params[i];
171 int variantType = QMetaType::type(typeName: type);
172 if (variantType >= QMetaType::User || variantType == QMetaType::QVariant) {
173 if (variantType != QMetaType::QVariant) {
174 // Wrap custom types in a QDBusVariant of the type name and
175 // a buffer of its variant-wrapped data
176 QByteArray buffer;
177 QDataStream stream(&buffer, QIODevice::ReadWrite | QIODevice::Append);
178 stream << args[i];
179
180 QServiceUserTypeDBus customType;
181 customType.typeName = type;
182 customType.variantBuffer = buffer;
183
184 QDBusVariant replacement(QVariant::fromValue(value: customType));
185 convertedList.replace(i, t: QVariant::fromValue(value: replacement));
186 }
187
188 sig.replace(before: QByteArray(type), after: QByteArray("QDBusVariant"));
189 }
190 }
191
192 // Activate the DBus object signal call
193 const int numArgs = convertedList.size();
194 QVarLengthArray<void *, 32> a( numArgs+1 );
195 a[0] = 0;
196
197 const QList<QByteArray> pTypes = method.parameterTypes();
198 for ( int arg = 0; arg < numArgs; ++arg ) {
199 if (pTypes.at(i: arg) == "QVariant")
200 a[arg+1] = (void *)&( convertedList[arg] );
201 else
202 a[arg+1] = (void *)( convertedList[arg].data() );
203 }
204
205 int dbusIndex = d->dbusMeta->indexOfSignal(signal: sig);
206 QMetaObject::activate(sender: this, signal_index: dbusIndex, argv: a.data());
207}
208
209
210/*!
211 Build a metaobject that represents the service object as a valid service that
212 satisfies the QtDBus type system.
213*/
214const QMetaObject* QServiceMetaObjectDBus::dbusMetaObject(bool signalsObject) const
215{
216 // Create a meta-object to represent all the contents of our service on DBus
217 QMetaObjectBuilder builder;
218
219 builder.setClassName(d->serviceMeta->className());
220 builder.setSuperClass(d->serviceMeta->superClass()); // needed?
221
222 const QMetaObject* mo = d->serviceMeta;
223 while (mo && strcmp(s1: mo->className(), s2: "QObject")) {
224 // Add our methods, signals and slots
225 for (int i = mo->methodOffset(); i < mo->methodCount(); i++) {
226 QMetaMethod mm = mo->method(index: i);
227
228 if (signalsObject && mm.methodType() != QMetaMethod::Signal)
229 continue;
230
231 // Convert QVariant and custom return types to QDBusVariants
232 QByteArray ret(mm.typeName());
233 const QByteArray& type = mm.typeName();
234 int variantType = QMetaType::type(typeName: type);
235 if (variantType >= QMetaType::User || variantType == QMetaType::QVariant) {
236 ret = QByteArray("QDBusVariant");
237 }
238
239 // Convert QVariant and custom argument types to QDBusVariants
240 QByteArray sig(mm.methodSignature());
241 const QList<QByteArray> pTypes = mm.parameterTypes();
242 const int pTypesCount = pTypes.count();
243 for (int i=0; i < pTypesCount; i++) {
244 const QByteArray& type = pTypes[i];
245 int variantType = QMetaType::type(typeName: type);
246 if (variantType >= QMetaType::User || variantType == QMetaType::QVariant) {
247 sig.replace(before: QByteArray(type), after: QByteArray("QDBusVariant"));
248 }
249 }
250
251 // Add a MetaMethod with converted signature to our builder
252 QMetaMethodBuilder method;
253 switch (mm.methodType()) {
254 case QMetaMethod::Method:
255 method = builder.addMethod(signature: sig);
256 break;
257 case QMetaMethod::Slot:
258 method = builder.addSlot(signature: sig);
259 break;
260 case QMetaMethod::Signal:
261 method = builder.addSignal(signature: sig);
262 break;
263 default:
264 break;
265 }
266
267 // Make sure our built MetaMethod is identical, excluding conversion
268 method.setReturnType(ret);
269 method.setParameterNames(mm.parameterNames());
270 method.setTag(mm.tag());
271 method.setAccess(mm.access());
272 method.setAttributes(mm.attributes());
273 }
274
275 if (signalsObject)
276 return builder.toMetaObject();
277
278 // Add our property accessor methods
279 // NOTE: required because read/reset properties over DBus require adaptors
280 // otherwise a metacall won't be invoked as QMetaObject::ReadProperty
281 // or QMetaObject::ResetProperty
282 QMetaMethodBuilder readProp;
283 readProp = builder.addMethod(signature: QByteArray("propertyRead(QString)"));
284 readProp.setReturnType(QByteArray("QDBusVariant"));
285 QList<QByteArray> params;
286 params << QByteArray("name");
287 readProp.setParameterNames(params);
288
289 QMetaMethodBuilder resetProp;
290 resetProp = builder.addMethod(signature: QByteArray("propertyReset(QString)"));
291 QList<QByteArray> paramsReset;
292 paramsReset << QByteArray("name");
293 resetProp.setParameterNames(paramsReset);
294
295
296 // Add our properties/enums
297 int propCount = d->serviceMeta->propertyCount();
298 for (int i=0; i<propCount; i++) {
299 QMetaProperty mp = d->serviceMeta->property(index: i);
300
301 QMetaPropertyBuilder property = builder.addProperty(name: mp.name(), type: mp.typeName());
302 property.setReadable(mp.isReadable());
303 property.setWritable(mp.isWritable());
304 property.setResettable(mp.isResettable());
305 property.setDesignable(mp.isDesignable());
306 property.setScriptable(mp.isScriptable());
307 property.setStored(mp.isStored());
308 property.setEditable(mp.isEditable());
309 property.setUser(mp.isUser());
310 property.setStdCppSet(mp.hasStdCppSet());
311 property.setEnumOrFlag(mp.isEnumType());
312 }
313
314 // Needs Enumerators support when QtDBus supports
315
316 mo = mo->superClass();
317 }
318
319 // return our constructed dbus metaobject
320 return builder.toMetaObject();
321}
322
323/*!
324 Provide custom Q_OBJECT implementation of the metaObject
325*/
326const QMetaObject* QServiceMetaObjectDBus::metaObject() const
327{
328 // Provide our construected DBus service metaobject
329 return d->dbusMeta;
330}
331
332/*!
333 Overrides metacall which relays the DBus service call to the actual service
334 meta object. Positive return will indicate that the metacall was unsuccessful
335*/
336int QServiceMetaObjectDBus::qt_metacall(QMetaObject::Call c, int id, void **a)
337{
338 int dbusIndex = id;
339
340 // Relay the meta-object call to the service object
341 if (c == QMetaObject::InvokeMetaMethod) {
342 // METHOD CALL
343 QMetaMethod method = d->dbusMeta->method(index: id);
344
345 const bool isSignal = (method.methodType() == QMetaMethod::Signal);
346
347 ///////////////////// CHECK SPECIAL PROPERTY ///////////////////////
348 // This is required because property READ/RESET doesn't function
349 // as desired over DBus without the use of adaptors. Temporary
350 // methods propertyRead and propertyReset are added to the published
351 // meta object and relay the correct property call
352 QString methodName(QLatin1String(method.methodSignature().constData()));
353 methodName.truncate(pos: methodName.indexOf(s: QLatin1String("(")));
354
355 if (methodName == QLatin1String("propertyRead")) {
356 QString propertyName = *reinterpret_cast<QString*>(a[1]);
357 int index = d->dbusMeta->indexOfProperty(name: propertyName.toLatin1().constData());
358 id = qt_metacall(c: QMetaObject::ReadProperty, id: index, a);
359 return id;
360
361 } else if (methodName == QLatin1String("propertyReset")) {
362 QString propertyName = *reinterpret_cast<QString*>(a[1]);
363 int index = d->dbusMeta->indexOfProperty(name: propertyName.toLatin1().constData());
364 id = qt_metacall(c: QMetaObject::ResetProperty, id: index, a);
365 return id;
366 }
367 ////////////////////////////////////////////////////////////////////
368
369 // Find the corresponding signature to our service object
370 QByteArray sig(method.methodSignature());
371 int count = methodName.size() + 1;
372 const QList<QByteArray> xTypes = method.parameterTypes();
373 const int xTypesCount = xTypes.count();
374 for (int i=0; i < xTypesCount; i++) {
375 const QByteArray& t = xTypes[i];
376 int variantType = QMetaType::type(typeName: t);
377
378 // Check for QVariants or custom types, represented as QDBusVariants
379 if (t == "QDBusVariant") {
380 // Convert QDBusVariant to QVariant
381 QVariant convert = QVariant(variantType, a[i+1]);
382 QDBusVariant dbusVariant = qvariant_cast<QDBusVariant>(v: convert);
383 QVariant variant = dbusVariant.variant();
384
385 // Is a custom argument if castable to QDBusArgument
386 bool hasCustomType = variant.canConvert<QDBusArgument>();
387 QByteArray replacement("QVariant");
388
389 // Custom types will have QDBusArgument streaming operators for
390 // the QServiceUserTypeDBus object. Extract the real type name
391 // and buffered QVariant from this
392 if (hasCustomType) {
393 QDBusArgument demarshall = variant.value<QDBusArgument>();
394 QServiceUserTypeDBus userType = qdbus_cast<QServiceUserTypeDBus>(arg: demarshall);
395 *reinterpret_cast<QVariant*>(a[i+1]) = userType.variantBuffer;
396
397 replacement = userType.typeName;
398 }
399
400 // Replace "QDBusVariant" with "QVariant" or custom type name
401 sig.replace(index: count, len: 12, s: replacement);
402 count += replacement.size();
403
404 } else {
405 // Supported type so skip this paramater
406 count += t.size();
407 }
408
409 // Skips the comma if not last parameter
410 if (i < xTypesCount)
411 count += 1;
412 }
413
414 // Find the corresponding method metaindex to our service object
415 id = d->serviceMeta->indexOfMethod(method: sig);
416 QMetaMethod mm = d->serviceMeta->method(index: id);
417
418 const QList<QByteArray> pTypes = mm.parameterTypes();
419 const int pTypesCount = pTypes.count();
420
421 const char* typeNames[] = {0,0,0,0,0,0,0,0,0,0};
422 const void* params[] = {0,0,0,0,0,0,0,0,0,0};
423 bool hasCustomType = false;
424
425 // Process arguments
426 for (int i=0; i < pTypesCount; i++) {
427 const QByteArray& t = pTypes[i];
428 int variantType = QMetaType::type(typeName: t);
429
430 if (variantType >= QMetaType::User) {
431 // Custom argument
432 QVariant convert = QVariant(QVariant::ByteArray, a[i+1]);
433 QByteArray buffer = convert.toByteArray();
434 QDataStream stream(&buffer, QIODevice::ReadWrite);
435
436 // Load our buffered variant-wrapped custom type
437 QVariant *customType = new QVariant(variantType, (const void*)0);
438 QMetaType::load(stream, type: QMetaType::QVariant, data: customType);
439
440 typeNames[i] = customType->typeName();
441 params[i] = customType->constData();
442 hasCustomType = true;
443 } else {
444 typeNames[i] = t.constData();
445 params[i] = a[i+1];
446 }
447 }
448
449 // Check if this is a signal emit and activate
450 if (isSignal) {
451 QMetaObject::activate(sender: this, signal_index: dbusIndex, argv: a);
452 return id;
453 }
454
455 // Check for custom return types and make the metacall
456 const QByteArray& type = mm.typeName();
457 int retType = QMetaType::type(typeName: type);
458 if (retType >= QMetaType::User) {
459 // Invoke the object method directly for custom return types
460 bool result = false;
461 QVariant returnValue = QVariant(retType, (const void*)0);
462 QGenericReturnArgument ret(type, returnValue.data());
463 result = mm.invoke(object: d->service, returnValue: ret,
464 val0: QGenericArgument(typeNames[0], params[0]),
465 val1: QGenericArgument(typeNames[1], params[1]),
466 val2: QGenericArgument(typeNames[2], params[2]),
467 val3: QGenericArgument(typeNames[3], params[3]),
468 val4: QGenericArgument(typeNames[4], params[4]),
469 val5: QGenericArgument(typeNames[5], params[5]),
470 val6: QGenericArgument(typeNames[6], params[6]),
471 val7: QGenericArgument(typeNames[7], params[7]),
472 val8: QGenericArgument(typeNames[8], params[8]),
473 val9: QGenericArgument(typeNames[9], params[9]));
474
475 if (result) {
476 // Wrap custom return type in a QDBusVariant of the type
477 // and a buffer of its variant-wrapped data
478 QByteArray buffer;
479 QDataStream stream(&buffer, QIODevice::WriteOnly | QIODevice::Append);
480 stream << returnValue;
481
482 QServiceUserTypeDBus customType;
483 customType.typeName = type;
484 customType.variantBuffer = buffer;
485
486 QDBusVariant replacement(QVariant::fromValue(value: customType));
487 *reinterpret_cast<QDBusVariant*>(a[0]) = replacement;
488
489 // Return negative id to say metacall was handled externally
490 return -1;
491 }
492
493 } else {
494 // Void or standard return types
495 if (hasCustomType == true) {
496 // Invoke the object method directly for custom arguments
497 bool result = false;
498 result = mm.invoke(object: d->service,
499 val0: QGenericArgument(typeNames[0], params[0]),
500 val1: QGenericArgument(typeNames[1], params[1]),
501 val2: QGenericArgument(typeNames[2], params[2]),
502 val3: QGenericArgument(typeNames[3], params[3]),
503 val4: QGenericArgument(typeNames[4], params[4]),
504 val5: QGenericArgument(typeNames[5], params[5]),
505 val6: QGenericArgument(typeNames[6], params[6]),
506 val7: QGenericArgument(typeNames[7], params[7]),
507 val8: QGenericArgument(typeNames[8], params[8]),
508 val9: QGenericArgument(typeNames[9], params[9]));
509 if (result) {
510 // Return negative id to say metacall was handled externally
511 return -1;
512 }
513 } else {
514 // Relay standard metacall to service object
515 id = d->service->qt_metacall(c, id, a);
516 }
517 }
518
519 } else {
520 // PROPERTY CALL
521
522 // Find the corresponding property metaindex of our service object
523 QMetaProperty property = d->dbusMeta->property(index: id);
524 QByteArray name(property.name());
525 id = d->serviceMeta->indexOfProperty(name);
526
527 if (c == QMetaObject::ReadProperty) {
528 // Convert to DBusVariant
529 QMetaProperty mp = d->serviceMeta->property(index: id);
530 QVariant value = mp.read(obj: d->service);
531 QDBusVariant replacement(value);
532 *reinterpret_cast<QDBusVariant*>(a[0]) = replacement;
533
534 // Return negative id to say metacall was handled externally
535 return -1;
536 }
537
538 // Metacall our service object property
539 id = d->service->qt_metacall(c, id, a);
540 }
541
542 return id;
543}
544
545void *QServiceMetaObjectDBus::qt_metacast(const char* className)
546{
547 if (!className) return 0;
548 //this object should not be castable to anything but QObject
549 return QObject::qt_metacast(className);
550}
551
552QDBusArgument &operator<<(QDBusArgument &argument, const QServiceUserTypeDBus &myType)
553{
554 argument.beginStructure();
555 argument << myType.typeName << myType.variantBuffer;
556 argument.endStructure();
557 return argument;
558}
559
560const QDBusArgument &operator>>(const QDBusArgument &argument, QServiceUserTypeDBus &myType)
561{
562 argument.beginStructure();
563 argument >> myType.typeName >> myType.variantBuffer;
564 argument.endStructure();
565 return argument;
566}
567
568QT_END_NAMESPACE
569

source code of qtsystems/src/serviceframework/ipc/qservicemetaobject_dbus.cpp