Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qdbusabstractinterface.cpp
Go to the documentation of this file.
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
7
8#include <qcoreapplication.h>
9#include <qthread.h>
10
11#include "qdbusargument.h"
12#include "qdbuspendingcall.h"
13#include "qdbusmessage_p.h"
14#include "qdbusmetaobject_p.h"
15#include "qdbusmetatype_p.h"
16#include "qdbusservicewatcher.h"
17#include "qdbusutil_p.h"
18
19#include <qdebug.h>
20
21#ifndef QT_NO_DBUS
22
24
25using namespace Qt::StringLiterals;
26
27namespace {
28// ### Qt6: change to a regular QEvent (customEvent)
29// We need to use a QMetaCallEvent here because we can't override customEvent() in
30// Qt 5. Since QDBusAbstractInterface is meant to be derived from, the vtables of
31// classes in generated code will have a pointer to QObject::customEvent instead
32// of to QDBusAbstractInterface::customEvent.
33// See solution in Patch Set 1 of this change in the Qt Gerrit servers.
34// (https://codereview.qt-project.org/#/c/126384/1)
35class DisconnectRelayEvent : public QAbstractMetaCallEvent
36{
37public:
38 DisconnectRelayEvent(QObject *sender, const QMetaMethod &m)
39 : QAbstractMetaCallEvent(sender, m.methodIndex())
40 {}
41
42 void placeMetaCall(QObject *object) override
43 {
46 }
47};
48}
49
50static QDBusError checkIfValid(const QString &service, const QString &path,
51 const QString &interface, bool isDynamic, bool isPeer)
52{
53 // We should be throwing exceptions here... oh well
55
56 // dynamic interfaces (QDBusInterface) can have empty interfaces, but not service and object paths
57 // non-dynamic is the opposite: service and object paths can be empty, but not the interface
58 if (!isDynamic) {
59 // use assertion here because this should never happen, at all
60 Q_ASSERT_X(!interface.isEmpty(), "QDBusAbstractInterface", "Interface name cannot be empty");
61 }
62 if (!QDBusUtil::checkBusName(service, (isDynamic && !isPeer) ? QDBusUtil::EmptyNotAllowed : QDBusUtil::EmptyAllowed, &error))
63 return error;
65 return error;
67 return error;
68
69 // no error
70 return QDBusError();
71}
72
74 const QString &p,
75 const QString &iface,
76 const QDBusConnection& con,
77 bool isDynamic)
78 : connection(con), service(serv), path(p), interface(iface),
79 lastError(checkIfValid(serv, p, iface, isDynamic, (connectionPrivate() &&
80 connectionPrivate()->mode == QDBusConnectionPrivate::PeerMode))),
81 timeout(-1),
82 isValid(!lastError.isValid())
83{
84 if (!isValid)
85 return;
86
87 if (!connection.isConnected()) {
90 }
91}
92
94{
95 if (!isValid || !connection.isConnected() || !connectionPrivate()->shouldWatchService(service))
96 return;
97
99 SIGNAL(serviceOwnerChanged(QString,QString,QString)),
101
103 if (currentOwner.isEmpty())
105}
106
108{
109 // recheck only if we have a wildcard (i.e. empty) service or path
110 // if any are empty, set the error message according to QDBusUtil
113 if (path.isEmpty())
115 return true;
116}
117
118bool QDBusAbstractInterfacePrivate::property(const QMetaProperty &mp, void *returnValuePtr) const
119{
120 if (!isValid || !canMakeCalls()) // can't make calls
121 return false;
122
123 QMetaType type = mp.metaType();
124 // is this metatype registered?
125 const char *expectedSignature = "";
126 if (type.id() != QMetaType::QVariant) {
127 expectedSignature = QDBusMetaType::typeToSignature(type);
128 if (expectedSignature == nullptr) {
129 qWarning("QDBusAbstractInterface: type %s must be registered with Qt D-Bus before it can be "
130 "used to read property %s.%s",
131 mp.typeName(), qPrintable(interface), mp.name());
132 lastError = QDBusError(QDBusError::Failed, "Unregistered type %1 cannot be handled"_L1
134 return false;
135 }
136 }
137
138 // try to read this property
141 QStringLiteral("Get"));
143 msg << interface << QString::fromUtf8(mp.name());
145
146 if (reply.type() != QDBusMessage::ReplyMessage) {
148 return false;
149 }
150 if (reply.signature() != "v"_L1) {
151 QString errmsg =
152 "Invalid signature '%1' in return from call to " DBUS_INTERFACE_PROPERTIES ""_L1;
153 lastError = QDBusError(QDBusError::InvalidSignature, std::move(errmsg).arg(reply.signature()));
154 return false;
155 }
156
157 QByteArray foundSignature;
158 const char *foundType = nullptr;
159 QVariant value = qvariant_cast<QDBusVariant>(reply.arguments().at(0)).variant();
160
161 if (value.metaType() == type || type.id() == QMetaType::QVariant
162 || (expectedSignature[0] == 'v' && expectedSignature[1] == '\0')) {
163 // simple match
164 if (type.id() == QMetaType::QVariant) {
165 *reinterpret_cast<QVariant*>(returnValuePtr) = value;
166 } else {
167 QMetaType(type).destruct(returnValuePtr);
168 QMetaType(type).construct(returnValuePtr, value.constData());
169 }
170 return true;
171 }
172
173 if (value.metaType() == QMetaType::fromType<QDBusArgument>()) {
174 QDBusArgument arg = qvariant_cast<QDBusArgument>(value);
175
176 foundType = "user type";
177 foundSignature = arg.currentSignature().toLatin1();
178 if (foundSignature == expectedSignature) {
179 // signatures match, we can demarshall
180 return QDBusMetaType::demarshall(arg, QMetaType(type), returnValuePtr);
181 }
182 } else {
183 foundType = value.typeName();
184 foundSignature = QDBusMetaType::typeToSignature(value.metaType());
185 }
186
187 // there was an error...
188 const auto errmsg = "Unexpected '%1' (%2) when retrieving property '%3.%4' "
189 "(expected type '%5' (%6))"_L1;
191 errmsg.arg(QLatin1StringView(foundType),
192 QLatin1StringView(foundSignature),
193 interface,
196 QLatin1StringView(expectedSignature)));
197 return false;
198}
199
201{
202 if (!isValid || !canMakeCalls()) // can't make calls
203 return false;
204
205 // send the value
208 QStringLiteral("Set"));
210 msg << interface << QString::fromUtf8(mp.name()) << QVariant::fromValue(QDBusVariant(value));
211 QDBusMessage reply = connection.call(msg, QDBus::Block, timeout);
212
213 if (reply.type() != QDBusMessage::ReplyMessage) {
214 lastError = QDBusError(reply);
215 return false;
216 }
217 return true;
218}
219
221 const QString &oldOwner,
222 const QString &newOwner)
223{
224 Q_UNUSED(oldOwner);
225 Q_UNUSED(name);
226 //qDebug() << "QDBusAbstractInterfacePrivate serviceOwnerChanged" << name << oldOwner << newOwner;
228 currentOwner = newOwner;
229}
230
232 : QObject(d, parent)
233{
234}
235
237{
238 int saved_id = _id;
239 _id = QObject::qt_metacall(_c, _id, _a);
240 if (_id < 0)
241 return _id;
242
244 QMetaProperty mp = metaObject()->property(saved_id);
245 int &status = *reinterpret_cast<int *>(_a[2]);
246
247 if (_c == QMetaObject::WriteProperty) {
249 if (mp.metaType() == QMetaType::fromType<QDBusVariant>())
250 value = reinterpret_cast<const QDBusVariant*>(_a[0])->variant();
251 else
252 value = QVariant(mp.metaType(), _a[0]);
253 status = d_func()->setProperty(mp, value) ? 1 : 0;
254 } else {
255 bool readStatus = d_func()->property(mp, _a[0]);
256 // Caller supports QVariant returns? Then we can also report errors
257 // by storing an invalid variant.
258 if (!readStatus && _a[1]) {
259 status = 0;
260 reinterpret_cast<QVariant*>(_a[1])->clear();
261 }
262 }
263 _id = -1;
264 }
265 return _id;
266}
267
292{
293 d.initOwnerTracking();
294}
295
302 const char *interface, const QDBusConnection &con,
305 con, false), parent)
306{
307 // keep track of the service owner
308 d_func()->initOwnerTracking();
309}
310
315{
316}
317
327{
328 Q_D(const QDBusAbstractInterface);
329 /* We don't retrieve the owner name for peer connections */
330 if (d->connectionPrivate() && d->connectionPrivate()->mode == QDBusConnectionPrivate::PeerMode) {
331 return d->isValid;
332 } else {
333 return !d->currentOwner.isEmpty();
334 }
335}
336
341{
342 return d_func()->connection;
343}
344
349{
350 return d_func()->service;
351}
352
357{
358 return d_func()->path;
359}
360
365{
366 return d_func()->interface;
367}
368
374{
375 return d_func()->lastError;
376}
377
385{
386 d_func()->timeout = timeout;
387}
388
396{
397 return d_func()->timeout;
398}
399
418 const QString& method,
419 const QList<QVariant>& args)
420{
422
423 if (!d->isValid || !d->canMakeCalls())
424 return QDBusMessage::createError(d->lastError);
425
426 QString m = method;
427 // split out the signature from the method
428 int pos = method.indexOf(u'.');
429 if (pos != -1)
430 m.truncate(pos);
431
432 if (mode == QDBus::AutoDetect) {
433 // determine if this a sync or async call
435 const QMetaObject *mo = metaObject();
436 QByteArray match = m.toLatin1();
437
438 for (int i = staticMetaObject.methodCount(); i < mo->methodCount(); ++i) {
439 QMetaMethod mm = mo->method(i);
440 if (mm.name() == match) {
441 // found a method with the same name as what we're looking for
442 // hopefully, nobody is overloading asynchronous and synchronous methods with
443 // the same name
444
445 QList<QByteArray> tags = QByteArray(mm.tag()).split(' ');
446 if (tags.contains("Q_NOREPLY"))
448
449 break;
450 }
451 }
452 }
453
454// qDebug() << "QDBusAbstractInterface" << "Service" << service() << "Path:" << path();
457 msg.setArguments(args);
458
459 QDBusMessage reply = d->connection.call(msg, mode, d->timeout);
461 d->lastError = QDBusError(reply); // will clear if reply isn't an error
462
463 // ensure that there is at least one element
464 if (reply.arguments().isEmpty())
465 reply << QVariant();
466
467 return reply;
468}
469
482 const QList<QVariant>& args)
483{
485
486 if (!d->isValid || !d->canMakeCalls())
487 return QDBusPendingCall::fromError(d->lastError);
488
491 msg.setArguments(args);
492 return d->connection.asyncCall(msg, d->timeout);
493}
494
517 const QList<QVariant> &args,
518 QObject *receiver,
519 const char *returnMethod,
520 const char *errorMethod)
521{
523
524 if (!d->isValid || !d->canMakeCalls())
525 return false;
526
528 path(),
529 interface(),
530 method);
532 msg.setArguments(args);
533
534 d->lastError = QDBusError();
535 return d->connection.callWithCallback(msg,
536 receiver,
537 returnMethod,
538 errorMethod,
539 d->timeout);
540}
541
561 const QList<QVariant> &args,
562 QObject *receiver,
563 const char *slot)
564{
565 return callWithCallback(method, args, receiver, slot, nullptr);
566}
567
573{
574 // someone connecting to one of our signals
576 if (!d->isValid)
577 return;
578
579 // we end up recursing here, so optimize away
581 if (signal == destroyedSignal)
582 return;
583
584 QDBusConnectionPrivate *conn = d->connectionPrivate();
585 if (conn) {
586 conn->connectRelay(d->service, d->path, d->interface,
587 this, signal);
588 }
589}
590
596{
597 // someone disconnecting from one of our signals
599 if (!d->isValid)
600 return;
601
602 // disconnection is just resource freeing, so it can be delayed;
603 // let's do that later, after all the QObject mutexes have been unlocked.
604 QCoreApplication::postEvent(this, new DisconnectRelayEvent(this, signal));
605}
606
612{
614 QDBusConnectionPrivate *conn = d->connectionPrivate();
615 if (!conn)
616 return;
617
618 const QMetaObject *mo = ptr->metaObject();
619 QMetaMethod signal = signalId >= 0 ? mo->method(signalId) : QMetaMethod();
620 if (signal.isValid()) {
621 if (!ptr->isSignalConnected(signal))
622 return conn->disconnectRelay(d->service, d->path, d->interface,
623 ptr, signal);
624 } else {
625 // wildcard disconnecting, we need to figure out which of our signals are
626 // no longer connected to anything
627 int midx = QObject::staticMetaObject.methodCount();
628 const int end = mo->methodCount();
629 for ( ; midx < end; ++midx) {
630 QMetaMethod mm = mo->method(midx);
631 if (mm.methodType() == QMetaMethod::Signal && !ptr->isSignalConnected(mm))
632 conn->disconnectRelay(d->service, d->path, d->interface, ptr, mm);
633 }
634 }
635}
636
642{
643 // assume this property exists and is readable
644 // we're only called from generated code anyways
645
646 return property(propname);
647}
648
654{
655 setProperty(propname, value);
656}
657
748 const QString &method,
749 const QList<QVariant> &args) const
750{
751 // ### move the code here, and make the other functions call this
752 return const_cast<QDBusAbstractInterface*>(this)->callWithArgumentList(mode, method, args);
753}
754
755QDBusMessage QDBusAbstractInterface::doCall(QDBus::CallMode mode, const QString &method, const QVariant *args, size_t numArgs)
756{
758 list.reserve(int(numArgs));
759 for (size_t i = 0; i < numArgs; ++i)
760 list.append(args[i]);
762}
763
764QDBusPendingCall QDBusAbstractInterface::doAsyncCall(const QString &method, const QVariant *args, size_t numArgs)
765{
767 list.reserve(int(numArgs));
768 for (size_t i = 0; i < numArgs; ++i)
769 list.append(args[i]);
771}
772
774
775#endif // QT_NO_DBUS
776
777#include "moc_qdbusabstractinterface.cpp"
virtual void placeMetaCall(QObject *object)=0
\inmodule QtCore
Definition qbytearray.h:57
QList< QByteArray > split(char sep) const
Splits the byte array into subarrays wherever sep occurs, and returns the list of those arrays.
static void postEvent(QObject *receiver, QEvent *event, int priority=Qt::NormalEventPriority)
int qt_metacall(QMetaObject::Call, int, void **) override
QDBusAbstractInterfaceBase(QDBusAbstractInterfacePrivate &dd, QObject *parent)
static void finishDisconnectNotify(QDBusAbstractInterface *iface, int signalId)
QDBusAbstractInterfacePrivate(const QString &serv, const QString &p, const QString &iface, const QDBusConnection &con, bool dynamic)
bool setProperty(const QMetaProperty &mp, const QVariant &value)
QDBusConnectionPrivate * connectionPrivate() const
bool property(const QMetaProperty &mp, void *returnValuePtr) const
void _q_serviceOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner)
QVariant internalPropGet(const char *propname) const
QDBusPendingCall asyncCallWithArgumentList(const QString &method, const QList< QVariant > &args)
QDBusAbstractInterface(const QString &service, const QString &path, const char *interface, const QDBusConnection &connection, QObject *parent)
QString service() const
Returns the name of the service this interface is associated with.
void setTimeout(int timeout)
Sets the timeout in milliseconds for all future DBus calls to timeout.
void disconnectNotify(const QMetaMethod &signal) override
QDBusConnection connection() const
Returns the connection this interface is associated with.
void internalPropSet(const char *propname, const QVariant &value)
QString interface() const
Returns the name of this interface.
QString path() const
Returns the object path that this interface is associated with.
QDBusMessage callWithArgumentList(QDBus::CallMode mode, const QString &method, const QList< QVariant > &args)
Places a call to the remote method specified by method on this interface, using args as arguments.
bool isValid() const
Returns true if this is a valid reference to a remote object.
int timeout() const
Returns the current value of the timeout in milliseconds.
QDBusError lastError() const
Returns the error the last operation produced, or an invalid error if the last operation did not prod...
void connectNotify(const QMetaMethod &signal) override
QDBusMessage internalConstCall(QDBus::CallMode mode, const QString &method, const QList< QVariant > &args=QList< QVariant >()) const
bool callWithCallback(const QString &method, const QList< QVariant > &args, QObject *receiver, const char *member, const char *errorSlot)
Places a call to the remote method specified by method on this interface, using args as arguments.
virtual ~QDBusAbstractInterface()
Releases this object's resources.
\inmodule QtDBus
void disconnectRelay(const QString &service, const QString &path, const QString &interface, QDBusAbstractInterface *receiver, const QMetaMethod &signal)
QString getNameOwner(const QString &service)
void connectRelay(const QString &service, const QString &path, const QString &interface, QDBusAbstractInterface *receiver, const QMetaMethod &signal)
\inmodule QtDBus
bool isConnected() const
Returns true if this QDBusConnection object is connected.
QDBusMessage call(const QDBusMessage &message, QDBus::CallMode mode=QDBus::Block, int timeout=-1) const
Sends the message over this connection and blocks, waiting for a reply, for at most timeout milliseco...
\inmodule QtDBus
Definition qdbuserror.h:21
@ InvalidSignature
Definition qdbuserror.h:43
static void setParametersValidated(QDBusMessage &msg, bool enable)
\inmodule QtDBus
void setArguments(const QList< QVariant > &arguments)
Sets the arguments that are going to be sent over D-Bus to arguments.
static QDBusMessage createError(const QString &name, const QString &msg)
Constructs a new DBus message representing an error, with the given name and msg.
static QDBusMessage createMethodCall(const QString &destination, const QString &path, const QString &interface, const QString &method)
Constructs a new DBus message representing a method call.
static bool demarshall(const QDBusArgument &, QMetaType id, void *data)
static const char * typeToSignature(QMetaType type)
\inmodule QtDBus
static QDBusPendingCall fromError(const QDBusError &error)
The QDBusServiceWatcher class allows the user to watch for a bus service change.
\inmodule QtDBus
QVariant variant() const
Returns this D-Bus variant as a QVariant object.
Definition qlist.h:74
void reserve(qsizetype size)
Definition qlist.h:746
void append(parameter_type t)
Definition qlist.h:441
\inmodule QtCore
Definition qmetaobject.h:18
static QMetaMethod fromSignal(PointerToMemberFunction signal)
const char * tag() const
Returns the tag associated with this method.
MethodType methodType() const
Returns the type of this method (signal, slot, or method).
QByteArray name() const
\inmodule QtCore
const char * typeName() const
Returns the name of this property's type.
QMetaType metaType() const
const char * name() const
Returns this property's name.
\inmodule QtCore
Definition qmetatype.h:320
void destruct(void *data) const
void * construct(void *where, const void *copy=nullptr) const
friend class QVariant
Definition qmetatype.h:775
\inmodule QtCore
Definition qobject.h:90
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2823
QThread * thread() const
Returns the thread in which the object lives.
Definition qobject.cpp:1561
bool setProperty(const char *name, const QVariant &value)
Sets the value of the object's name property to value.
void destroyed(QObject *=nullptr)
This signal is emitted immediately before the object obj is destroyed, after any instances of QPointe...
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5857
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
static QThread * currentThread()
Definition qthread.cpp:966
\inmodule QtCore
Definition qvariant.h:64
#define DBUS_INTERFACE_PROPERTIES
b clear()
auto signal
auto mo
[7]
bool checkInterfaceName(const QString &name, AllowEmptyFlag empty, QDBusError *error)
Definition qdbusutil_p.h:62
QString dbusInterfaceProperties()
bool checkObjectPath(const QString &path, AllowEmptyFlag empty, QDBusError *error)
Definition qdbusutil_p.h:86
@ EmptyNotAllowed
Definition qdbusutil_p.h:59
bool checkBusName(const QString &name, AllowEmptyFlag empty, QDBusError *error)
Definition qdbusutil_p.h:74
QString disconnectedErrorMessage()
CallMode
This enum describes the various ways of placing a function call.
Combined button and popup list for selecting options.
constexpr QBindableInterface iface
Definition qproperty.h:664
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char * interface
DBusConnection const char DBusError * error
DBusConnection * connection
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char * method
static QDBusError checkIfValid(const QString &service, const QString &path, const QString &interface, bool isDynamic, bool isPeer)
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define qWarning
Definition qlogging.h:162
static ControlElement< T > * ptr(QWidget *widget)
#define SLOT(a)
Definition qobjectdefs.h:51
#define SIGNAL(a)
Definition qobjectdefs.h:52
GLenum mode
const GLfloat * m
GLuint GLuint end
GLuint object
[3]
GLbitfield GLuint64 timeout
[4]
GLenum type
GLuint name
GLsizei const GLchar *const * path
GLfloat GLfloat p
[1]
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_ASSERT_X(cond, x, msg)
Definition qrandom.cpp:48
SSL_CTX int(*) void arg)
#define qPrintable(string)
Definition qstring.h:1391
#define QStringLiteral(str)
#define Q_UNUSED(x)
static bool match(const uchar *found, uint foundLen, const char *target, uint targetLen)
const char property[13]
Definition qwizard.cpp:101
QList< int > list
[14]
if(qFloatDistance(a, b)<(1<< 7))
[0]
obj metaObject() -> className()
QNetworkReply * reply
QJSValueList args
bool contains(const AT &t) const noexcept
Definition qlist.h:44
\inmodule QtCore
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent