1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtDBus module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qdbusabstractinterface.h"
43 #include "qdbusabstractinterface_p.h"
47 #include "qdbusargument.h"
48 #include "qdbuspendingcall.h"
49 #include "qdbusmessage_p.h"
50 #include "qdbusmetaobject_p.h"
51 #include "qdbusmetatype_p.h"
52 #include "qdbusutil_p.h"
60 static QDBusError checkIfValid(const QString &service, const QString &path,
61 const QString &interface, bool isDynamic, bool isPeer)
63 // We should be throwing exceptions here... oh well
66 // dynamic interfaces (QDBusInterface) can have empty interfaces, but not service and object paths
67 // non-dynamic is the opposite: service and object paths can be empty, but not the interface
69 // use assertion here because this should never happen, at all
70 Q_ASSERT_X(!interface.isEmpty(), "QDBusAbstractInterface", "Interface name cannot be empty");
72 if (!QDBusUtil::checkBusName(service, (isDynamic && !isPeer) ? QDBusUtil::EmptyNotAllowed : QDBusUtil::EmptyAllowed, &error))
74 if (!QDBusUtil::checkObjectPath(path, isDynamic ? QDBusUtil::EmptyNotAllowed : QDBusUtil::EmptyAllowed, &error))
76 if (!QDBusUtil::checkInterfaceName(interface, QDBusUtil::EmptyAllowed, &error))
83 QDBusAbstractInterfacePrivate::QDBusAbstractInterfacePrivate(const QString &serv,
86 const QDBusConnection& con,
88 : connection(con), service(serv), path(p), interface(iface),
89 lastError(checkIfValid(serv, p, iface, isDynamic, (connectionPrivate() &&
90 connectionPrivate()->mode == QDBusConnectionPrivate::PeerMode))),
92 isValid(!lastError.isValid())
97 if (!connection.isConnected()) {
98 lastError = QDBusError(QDBusError::Disconnected,
99 QLatin1String("Not connected to D-Bus server"));
100 } else if (!service.isEmpty()) {
101 currentOwner = connectionPrivate()->getNameOwner(service); // verify the name owner
102 if (currentOwner.isEmpty()) {
103 lastError = connectionPrivate()->lastError;
108 bool QDBusAbstractInterfacePrivate::canMakeCalls() const
110 // recheck only if we have a wildcard (i.e. empty) service or path
111 // if any are empty, set the error message according to QDBusUtil
112 if (service.isEmpty() && connectionPrivate()->mode != QDBusConnectionPrivate::PeerMode)
113 return QDBusUtil::checkBusName(service, QDBusUtil::EmptyNotAllowed, &lastError);
115 return QDBusUtil::checkObjectPath(path, QDBusUtil::EmptyNotAllowed, &lastError);
119 void QDBusAbstractInterfacePrivate::property(const QMetaProperty &mp, QVariant &where) const
121 if (!isValid || !canMakeCalls()) { // can't make calls
126 // is this metatype registered?
127 const char *expectedSignature = "";
128 if (int(mp.type()) != QMetaType::QVariant) {
129 expectedSignature = QDBusMetaType::typeToSignature(where.userType());
130 if (expectedSignature == 0) {
131 qWarning("QDBusAbstractInterface: type %s must be registered with QtDBus before it can be "
132 "used to read property %s.%s",
133 mp.typeName(), qPrintable(interface), mp.name());
134 lastError = QDBusError(QDBusError::Failed,
135 QString::fromLatin1("Unregistered type %1 cannot be handled")
136 .arg(QLatin1String(mp.typeName())));
142 // try to read this property
143 QDBusMessage msg = QDBusMessage::createMethodCall(service, path,
144 QLatin1String(DBUS_INTERFACE_PROPERTIES),
145 QLatin1String("Get"));
146 QDBusMessagePrivate::setParametersValidated(msg, true);
147 msg << interface << QString::fromUtf8(mp.name());
148 QDBusMessage reply = connection.call(msg, QDBus::Block, timeout);
150 if (reply.type() != QDBusMessage::ReplyMessage) {
151 lastError = QDBusError(reply);
155 if (reply.signature() != QLatin1String("v")) {
156 QString errmsg = QLatin1String("Invalid signature `%1' in return from call to "
157 DBUS_INTERFACE_PROPERTIES);
158 lastError = QDBusError(QDBusError::InvalidSignature, errmsg.arg(reply.signature()));
163 QByteArray foundSignature;
164 const char *foundType = 0;
165 QVariant value = qvariant_cast<QDBusVariant>(reply.arguments().at(0)).variant();
167 if (value.userType() == where.userType() || mp.userType() == QMetaType::QVariant
168 || (expectedSignature[0] == 'v' && expectedSignature[1] == '\0')) {
174 if (value.userType() == qMetaTypeId<QDBusArgument>()) {
175 QDBusArgument arg = qvariant_cast<QDBusArgument>(value);
177 foundType = "user type";
178 foundSignature = arg.currentSignature().toLatin1();
179 if (foundSignature == expectedSignature) {
180 // signatures match, we can demarshall
181 QDBusMetaType::demarshall(arg, where.userType(), where.data());
185 foundType = value.typeName();
186 foundSignature = QDBusMetaType::typeToSignature(value.userType());
189 // there was an error...
190 QString errmsg = QLatin1String("Unexpected `%1' (%2) when retrieving property `%3.%4' "
191 "(expected type `%5' (%6))");
192 lastError = QDBusError(QDBusError::InvalidSignature,
193 errmsg.arg(QString::fromLatin1(foundType),
194 QString::fromLatin1(foundSignature),
196 QString::fromUtf8(mp.name()),
197 QString::fromLatin1(mp.typeName()),
198 QString::fromLatin1(expectedSignature)));
203 bool QDBusAbstractInterfacePrivate::setProperty(const QMetaProperty &mp, const QVariant &value)
205 if (!isValid || !canMakeCalls()) // can't make calls
209 QDBusMessage msg = QDBusMessage::createMethodCall(service, path,
210 QLatin1String(DBUS_INTERFACE_PROPERTIES),
211 QLatin1String("Set"));
212 QDBusMessagePrivate::setParametersValidated(msg, true);
213 msg << interface << QString::fromUtf8(mp.name()) << QVariant::fromValue(QDBusVariant(value));
214 QDBusMessage reply = connection.call(msg, QDBus::Block, timeout);
216 if (reply.type() != QDBusMessage::ReplyMessage) {
217 lastError = QDBusError(reply);
223 void QDBusAbstractInterfacePrivate::_q_serviceOwnerChanged(const QString &name,
224 const QString &oldOwner,
225 const QString &newOwner)
228 //qDebug() << "QDBusAbstractInterfacePrivate serviceOwnerChanged" << name << oldOwner << newOwner;
229 if (name == service) {
230 currentOwner = newOwner;
234 QDBusAbstractInterfaceBase::QDBusAbstractInterfaceBase(QDBusAbstractInterfacePrivate &d, QObject *parent)
239 int QDBusAbstractInterfaceBase::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
242 _id = QObject::qt_metacall(_c, _id, _a);
246 if (_c == QMetaObject::ReadProperty || _c == QMetaObject::WriteProperty) {
247 QMetaProperty mp = metaObject()->property(saved_id);
248 int &status = *reinterpret_cast<int *>(_a[2]);
249 QVariant &variant = *reinterpret_cast<QVariant *>(_a[1]);
251 if (_c == QMetaObject::WriteProperty) {
252 status = d_func()->setProperty(mp, variant) ? 1 : 0;
254 d_func()->property(mp, variant);
255 status = variant.isValid() ? 1 : 0;
263 \class QDBusAbstractInterface
267 \brief The QDBusAbstractInterface class is the base class for all D-Bus interfaces in the QtDBus binding, allowing access to remote interfaces
269 Generated-code classes also derive from QDBusAbstractInterface,
270 all methods described here are also valid for generated-code
271 classes. In addition to those described here, generated-code
272 classes provide member functions for the remote methods, which
273 allow for compile-time checking of the correct parameters and
274 return values, as well as property type-matching and signal
277 \sa {qdbusxml2cpp.html}{The QDBus compiler}, QDBusInterface
282 This is the constructor called from QDBusInterface::QDBusInterface.
284 QDBusAbstractInterface::QDBusAbstractInterface(QDBusAbstractInterfacePrivate &d, QObject *parent)
285 : QDBusAbstractInterfaceBase(d, parent)
287 // keep track of the service owner
289 d.connection.isConnected()
290 && !d.service.isEmpty()
291 && !d.service.startsWith(QLatin1Char(':')))
292 d_func()->connection.connect(QLatin1String(DBUS_SERVICE_DBUS), // service
294 QLatin1String(DBUS_INTERFACE_DBUS), // interface
295 QLatin1String("NameOwnerChanged"),
296 QStringList() << d.service,
297 QString(), // signature
298 this, SLOT(_q_serviceOwnerChanged(QString,QString,QString)));
303 This is the constructor called from static classes derived from
304 QDBusAbstractInterface (i.e., those generated by dbusxml2cpp).
306 QDBusAbstractInterface::QDBusAbstractInterface(const QString &service, const QString &path,
307 const char *interface, const QDBusConnection &con,
309 : QDBusAbstractInterfaceBase(*new QDBusAbstractInterfacePrivate(service, path, QString::fromLatin1(interface),
312 // keep track of the service owner
313 if (d_func()->isValid &&
314 d_func()->connection.isConnected()
315 && !service.isEmpty()
316 && !service.startsWith(QLatin1Char(':')))
317 d_func()->connection.connect(QLatin1String(DBUS_SERVICE_DBUS), // service
319 QLatin1String(DBUS_INTERFACE_DBUS), // interface
320 QLatin1String("NameOwnerChanged"),
321 QStringList() << service,
322 QString(), //signature
323 this, SLOT(_q_serviceOwnerChanged(QString,QString,QString)));
327 Releases this object's resources.
329 QDBusAbstractInterface::~QDBusAbstractInterface()
334 Returns true if this is a valid reference to a remote object. It returns false if
335 there was an error during the creation of this interface (for instance, if the remote
336 application does not exist).
338 Note: when dealing with remote objects, it is not always possible to determine if it
339 exists when creating a QDBusInterface.
341 bool QDBusAbstractInterface::isValid() const
343 return !d_func()->currentOwner.isEmpty();
347 Returns the connection this interface is assocated with.
349 QDBusConnection QDBusAbstractInterface::connection() const
351 return d_func()->connection;
355 Returns the name of the service this interface is associated with.
357 QString QDBusAbstractInterface::service() const
359 return d_func()->service;
363 Returns the object path that this interface is associated with.
365 QString QDBusAbstractInterface::path() const
367 return d_func()->path;
371 Returns the name of this interface.
373 QString QDBusAbstractInterface::interface() const
375 return d_func()->interface;
379 Returns the error the last operation produced, or an invalid error if the last operation did not
382 QDBusError QDBusAbstractInterface::lastError() const
384 return d_func()->lastError;
388 Sets the timeout in milliseconds for all future DBus calls to \a timeout.
389 -1 means the default DBus timeout (usually 25 seconds).
393 void QDBusAbstractInterface::setTimeout(int timeout)
395 d_func()->timeout = timeout;
399 Returns the current value of the timeout in milliseconds.
400 -1 means the default DBus timeout (usually 25 seconds).
404 int QDBusAbstractInterface::timeout() const
406 return d_func()->timeout;
410 Places a call to the remote method specified by \a method on this interface, using \a args as
411 arguments. This function returns the message that was received as a reply, which can be a normal
412 QDBusMessage::ReplyMessage (indicating success) or QDBusMessage::ErrorMessage (if the call
413 failed). The \a mode parameter specifies how this call should be placed.
415 If the call succeeds, lastError() will be cleared; otherwise, it will contain the error this
418 Normally, you should place calls using call().
420 \warning If you use \c UseEventLoop, your code must be prepared to deal with any reentrancy:
421 other method calls and signals may be delivered before this function returns, as well
422 as other Qt queued signals and events.
426 QDBusMessage QDBusAbstractInterface::callWithArgumentList(QDBus::CallMode mode,
427 const QString& method,
428 const QList<QVariant>& args)
430 Q_D(QDBusAbstractInterface);
432 if (!d->isValid || !d->canMakeCalls())
433 return QDBusMessage::createError(d->lastError);
436 // split out the signature from the method
437 int pos = method.indexOf(QLatin1Char('.'));
441 if (mode == QDBus::AutoDetect) {
442 // determine if this a sync or async call
444 const QMetaObject *mo = metaObject();
445 QByteArray match = m.toLatin1();
447 for (int i = staticMetaObject.methodCount(); i < mo->methodCount(); ++i) {
448 QMetaMethod mm = mo->method(i);
449 if (mm.name() == match) {
450 // found a method with the same name as what we're looking for
451 // hopefully, nobody is overloading asynchronous and synchronous methods with
454 QList<QByteArray> tags = QByteArray(mm.tag()).split(' ');
455 if (tags.contains("Q_NOREPLY"))
456 mode = QDBus::NoBlock;
463 // qDebug() << "QDBusAbstractInterface" << "Service" << service() << "Path:" << path();
464 QDBusMessage msg = QDBusMessage::createMethodCall(service(), path(), interface(), m);
465 QDBusMessagePrivate::setParametersValidated(msg, true);
466 msg.setArguments(args);
468 QDBusMessage reply = d->connection.call(msg, mode, d->timeout);
469 if (thread() == QThread::currentThread())
470 d->lastError = QDBusError(reply); // will clear if reply isn't an error
472 // ensure that there is at least one element
473 if (reply.arguments().isEmpty())
481 Places a call to the remote method specified by \a method on this
482 interface, using \a args as arguments. This function returns a
483 QDBusPendingCall object that can be used to track the status of the
484 reply and access its contents once it has arrived.
486 Normally, you should place calls using asyncCall().
490 QDBusPendingCall QDBusAbstractInterface::asyncCallWithArgumentList(const QString& method,
491 const QList<QVariant>& args)
493 Q_D(QDBusAbstractInterface);
495 if (!d->isValid || !d->canMakeCalls())
496 return QDBusPendingCall::fromError(d->lastError);
498 QDBusMessage msg = QDBusMessage::createMethodCall(service(), path(), interface(), method);
499 QDBusMessagePrivate::setParametersValidated(msg, true);
500 msg.setArguments(args);
501 return d->connection.asyncCall(msg, d->timeout);
505 Places a call to the remote method specified by \a method
506 on this interface, using \a args as arguments. This function
507 returns immediately after queueing the call. The reply from
508 the remote function is delivered to the \a returnMethod on
509 object \a receiver. If an error occurs, the \a errorMethod
510 on object \a receiver is called instead.
512 This function returns true if the queueing succeeds. It does
513 not indicate that the executed call succeeded. If it fails,
514 the \a errorMethod is called. If the queueing failed, this
515 function returns false and no slot will be called.
517 The \a returnMethod must have as its parameters the types returned
518 by the function call. Optionally, it may have a QDBusMessage
519 parameter as its last or only parameter. The \a errorMethod must
520 have a QDBusError as its only parameter.
523 \sa QDBusError, QDBusMessage
525 bool QDBusAbstractInterface::callWithCallback(const QString &method,
526 const QList<QVariant> &args,
528 const char *returnMethod,
529 const char *errorMethod)
531 Q_D(QDBusAbstractInterface);
533 if (!d->isValid || !d->canMakeCalls())
536 QDBusMessage msg = QDBusMessage::createMethodCall(service(),
540 QDBusMessagePrivate::setParametersValidated(msg, true);
541 msg.setArguments(args);
543 d->lastError = QDBusError();
544 return d->connection.callWithCallback(msg,
554 This function is deprecated. Please use the overloaded version.
556 Places a call to the remote method specified by \a method
557 on this interface, using \a args as arguments. This function
558 returns immediately after queueing the call. The reply from
559 the remote function or any errors emitted by it are delivered
560 to the \a slot slot on object \a receiver.
562 This function returns true if the queueing succeeded: it does
563 not indicate that the call succeeded. If it failed, the slot
564 will be called with an error message. lastError() will not be
565 set under those circumstances.
567 \sa QDBusError, QDBusMessage
569 bool QDBusAbstractInterface::callWithCallback(const QString &method,
570 const QList<QVariant> &args,
574 return callWithCallback(method, args, receiver, slot, 0);
579 Catch signal connections.
581 void QDBusAbstractInterface::connectNotify(const QMetaMethod &signal)
583 // someone connecting to one of our signals
584 Q_D(QDBusAbstractInterface);
588 // we end up recursing here, so optimize away
589 static const QMetaMethod destroyedSignal = QMetaMethod::fromSignal(&QDBusAbstractInterface::destroyed);
590 if (signal == destroyedSignal)
593 QDBusConnectionPrivate *conn = d->connectionPrivate();
595 conn->connectRelay(d->service, d->path, d->interface,
602 Catch signal disconnections.
604 void QDBusAbstractInterface::disconnectNotify(const QMetaMethod &signal)
606 // someone disconnecting from one of our signals
607 Q_D(QDBusAbstractInterface);
611 QDBusConnectionPrivate *conn = d->connectionPrivate();
613 conn->disconnectRelay(d->service, d->path, d->interface,
619 Get the value of the property \a propname.
621 QVariant QDBusAbstractInterface::internalPropGet(const char *propname) const
623 // assume this property exists and is readable
624 // we're only called from generated code anyways
626 return property(propname);
631 Set the value of the property \a propname to \a value.
633 void QDBusAbstractInterface::internalPropSet(const char *propname, const QVariant &value)
635 setProperty(propname, value);
639 Calls the method \a method on this interface and passes the parameters to this function to the
642 The parameters to \c call are passed on to the remote function via D-Bus as input
643 arguments. Output arguments are returned in the QDBusMessage reply. If the reply is an error
644 reply, lastError() will also be set to the contents of the error message.
646 This function can be used with up to 8 parameters, passed in arguments \a arg1, \a arg2,
647 \a arg3, \a arg4, \a arg5, \a arg6, \a arg7 and \a arg8. If you need more than 8
648 parameters or if you have a variable number of parameters to be passed, use
649 callWithArgumentList().
651 It can be used the following way:
653 \snippet doc/src/snippets/code/src_qdbus_qdbusabstractinterface.cpp 0
655 This example illustrates function calling with 0, 1 and 2 parameters and illustrates different
656 parameter types passed in each (the first call to \c "ProcessWorkUnicode" will contain one
657 Unicode string, the second call to \c "ProcessWork" will contain one string and one byte array).
659 QDBusMessage QDBusAbstractInterface::call(const QString &method, const QVariant &arg1,
660 const QVariant &arg2,
661 const QVariant &arg3,
662 const QVariant &arg4,
663 const QVariant &arg5,
664 const QVariant &arg6,
665 const QVariant &arg7,
666 const QVariant &arg8)
668 return call(QDBus::AutoDetect, method, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
674 Calls the method \a method on this interface and passes the
675 parameters to this function to the method. If \a mode is \c
676 NoWaitForReply, then this function will return immediately after
677 placing the call, without waiting for a reply from the remote
678 method. Otherwise, \a mode indicates whether this function should
679 activate the Qt Event Loop while waiting for the reply to arrive.
681 This function can be used with up to 8 parameters, passed in arguments \a arg1, \a arg2,
682 \a arg3, \a arg4, \a arg5, \a arg6, \a arg7 and \a arg8. If you need more than 8
683 parameters or if you have a variable number of parameters to be passed, use
684 callWithArgumentList().
686 If this function reenters the Qt event loop in order to wait for the
687 reply, it will exclude user input. During the wait, it may deliver
688 signals and other method calls to your application. Therefore, it
689 must be prepared to handle a reentrancy whenever a call is placed
692 QDBusMessage QDBusAbstractInterface::call(QDBus::CallMode mode, const QString &method,
693 const QVariant &arg1,
694 const QVariant &arg2,
695 const QVariant &arg3,
696 const QVariant &arg4,
697 const QVariant &arg5,
698 const QVariant &arg6,
699 const QVariant &arg7,
700 const QVariant &arg8)
702 QList<QVariant> argList;
703 int count = 0 + arg1.isValid() + arg2.isValid() + arg3.isValid() + arg4.isValid() +
704 arg5.isValid() + arg6.isValid() + arg7.isValid() + arg8.isValid();
708 argList.prepend(arg8);
710 argList.prepend(arg7);
712 argList.prepend(arg6);
714 argList.prepend(arg5);
716 argList.prepend(arg4);
718 argList.prepend(arg3);
720 argList.prepend(arg2);
722 argList.prepend(arg1);
725 return callWithArgumentList(mode, method, argList);
731 Calls the method \a method on this interface and passes the parameters to this function to the
734 The parameters to \c call are passed on to the remote function via D-Bus as input
735 arguments. The returned QDBusPendingCall object can be used to find out information about
738 This function can be used with up to 8 parameters, passed in arguments \a arg1, \a arg2,
739 \a arg3, \a arg4, \a arg5, \a arg6, \a arg7 and \a arg8. If you need more than 8
740 parameters or if you have a variable number of parameters to be passed, use
741 asyncCallWithArgumentList().
743 It can be used the following way:
745 \snippet doc/src/snippets/code/src_qdbus_qdbusabstractinterface.cpp 1
747 This example illustrates function calling with 0, 1 and 2 parameters and illustrates different
748 parameter types passed in each (the first call to \c "ProcessWorkUnicode" will contain one
749 Unicode string, the second call to \c "ProcessWork" will contain one string and one byte array).
751 QDBusPendingCall QDBusAbstractInterface::asyncCall(const QString &method, const QVariant &arg1,
752 const QVariant &arg2,
753 const QVariant &arg3,
754 const QVariant &arg4,
755 const QVariant &arg5,
756 const QVariant &arg6,
757 const QVariant &arg7,
758 const QVariant &arg8)
760 QList<QVariant> argList;
761 int count = 0 + arg1.isValid() + arg2.isValid() + arg3.isValid() + arg4.isValid() +
762 arg5.isValid() + arg6.isValid() + arg7.isValid() + arg8.isValid();
766 argList.prepend(arg8);
768 argList.prepend(arg7);
770 argList.prepend(arg6);
772 argList.prepend(arg5);
774 argList.prepend(arg4);
776 argList.prepend(arg3);
778 argList.prepend(arg2);
780 argList.prepend(arg1);
783 return asyncCallWithArgumentList(method, argList);
789 QDBusMessage QDBusAbstractInterface::internalConstCall(QDBus::CallMode mode,
790 const QString &method,
791 const QList<QVariant> &args) const
793 // ### move the code here, and make the other functions call this
794 return const_cast<QDBusAbstractInterface*>(this)->callWithArgumentList(mode, method, args);
801 #include "moc_qdbusabstractinterface.cpp"