File indexing completed on 2024-12-22 04:33:56
0001 //SPDX-FileCopyrightText: 2015 Jolla Ltd. <valerio.valerio@jolla.com> 0002 //SPDX-FileContributor: Andres Gomez 0003 // 0004 //SPDX-License-Identifier: LGPL-2.1-or-later 0005 0006 0007 #include "dbusextendedpendingcallwatcher_p.h" 0008 0009 #include <dbusextendedabstractinterface.h> 0010 0011 #include <QtDBus/QDBusMessage> 0012 #include <QtDBus/QDBusPendingCall> 0013 #include <QtDBus/QDBusPendingCallWatcher> 0014 #include <QtDBus/QDBusPendingReply> 0015 0016 #include <QtCore/QDebug> 0017 #include <QtCore/QMetaProperty> 0018 0019 0020 Q_GLOBAL_STATIC_WITH_ARGS(QByteArray, dBusPropertiesInterface, ("org.freedesktop.DBus.Properties")) 0021 Q_GLOBAL_STATIC_WITH_ARGS(QByteArray, dBusPropertiesChangedSignal, ("PropertiesChanged")) 0022 Q_GLOBAL_STATIC_WITH_ARGS(QByteArray, propertyChangedSignature, ("propertyChanged(QString,QVariant)")) 0023 Q_GLOBAL_STATIC_WITH_ARGS(QByteArray, propertyInvalidatedSignature, ("propertyInvalidated(QString)")) 0024 0025 0026 DBusExtendedAbstractInterface::DBusExtendedAbstractInterface(const QString &service, const QString &path, const char *interface, const QDBusConnection &connection, QObject *parent) 0027 : QDBusAbstractInterface(service, path, interface, connection, parent) 0028 , m_sync(false) 0029 , m_useCache(false) 0030 , m_getAllPendingCallWatcher(0) 0031 , m_propertiesChangedConnected(false) 0032 { 0033 } 0034 0035 DBusExtendedAbstractInterface::~DBusExtendedAbstractInterface() 0036 { 0037 } 0038 0039 void DBusExtendedAbstractInterface::getAllProperties() 0040 { 0041 m_lastExtendedError = QDBusError(); 0042 0043 if (!isValid()) { 0044 QString errorMessage = QStringLiteral("This Extended DBus interface is not valid yet."); 0045 m_lastExtendedError = QDBusMessage::createError(QDBusError::Failed, errorMessage); 0046 qDebug() << Q_FUNC_INFO << errorMessage; 0047 return; 0048 } 0049 0050 if (!m_sync && m_getAllPendingCallWatcher) { 0051 // Call already in place, not repeating ... 0052 return; 0053 } 0054 0055 QDBusMessage msg = QDBusMessage::createMethodCall(service(), path(), *dBusPropertiesInterface(), QStringLiteral("GetAll")); 0056 msg << interface(); 0057 0058 if (m_sync) { 0059 QDBusMessage reply = connection().call(msg); 0060 0061 if (reply.type() != QDBusMessage::ReplyMessage) { 0062 m_lastExtendedError = QDBusError(reply); 0063 qWarning() << Q_FUNC_INFO << m_lastExtendedError.message(); 0064 return; 0065 } 0066 0067 if (reply.signature() != QLatin1String("a{sv}")) { 0068 QString errorMessage = QStringLiteral("Invalid signature \"%1\" in return from call to %2") 0069 .arg(reply.signature(), 0070 QString(*dBusPropertiesInterface())); 0071 qWarning() << Q_FUNC_INFO << errorMessage; 0072 m_lastExtendedError = QDBusError(QDBusError::InvalidSignature, errorMessage); 0073 return; 0074 } 0075 0076 QVariantMap value = reply.arguments().at(0).toMap(); 0077 onPropertiesChanged(interface(), value, QStringList()); 0078 } else { 0079 QDBusPendingReply<QVariantMap> async = connection().asyncCall(msg); 0080 m_getAllPendingCallWatcher = new QDBusPendingCallWatcher(async, this); 0081 0082 connect(m_getAllPendingCallWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(onAsyncGetAllPropertiesFinished(QDBusPendingCallWatcher*))); 0083 return; 0084 } 0085 } 0086 0087 void DBusExtendedAbstractInterface::connectNotify(const QMetaMethod &signal) 0088 { 0089 if (signal.methodType() == QMetaMethod::Signal 0090 && (signal.methodSignature() == *propertyChangedSignature() 0091 || signal.methodSignature() == *propertyInvalidatedSignature())) { 0092 if (!m_propertiesChangedConnected) { 0093 QStringList argumentMatch; 0094 argumentMatch << interface(); 0095 connection().connect(service(), path(), *dBusPropertiesInterface(), *dBusPropertiesChangedSignal(), 0096 argumentMatch, QString(), 0097 this, SLOT(onPropertiesChanged(QString, QVariantMap, QStringList))); 0098 0099 m_propertiesChangedConnected = true; 0100 return; 0101 } 0102 } else { 0103 QDBusAbstractInterface::connectNotify(signal); 0104 } 0105 } 0106 0107 void DBusExtendedAbstractInterface::disconnectNotify(const QMetaMethod &signal) 0108 { 0109 if (signal.methodType() == QMetaMethod::Signal 0110 && (signal.methodSignature() == *propertyChangedSignature() 0111 || signal.methodSignature() == *propertyInvalidatedSignature())) { 0112 if (m_propertiesChangedConnected 0113 && 0 == receivers(propertyChangedSignature()->constData()) 0114 && 0 == receivers(propertyInvalidatedSignature()->constData())) { 0115 QStringList argumentMatch; 0116 argumentMatch << interface(); 0117 connection().disconnect(service(), path(), *dBusPropertiesInterface(), *dBusPropertiesChangedSignal(), 0118 argumentMatch, QString(), 0119 this, SLOT(onPropertiesChanged(QString, QVariantMap, QStringList))); 0120 0121 m_propertiesChangedConnected = false; 0122 return; 0123 } 0124 } else { 0125 QDBusAbstractInterface::disconnectNotify(signal); 0126 } 0127 } 0128 0129 QVariant DBusExtendedAbstractInterface::internalPropGet(const char *propname, void *propertyPtr) 0130 { 0131 m_lastExtendedError = QDBusError(); 0132 0133 if (m_useCache) { 0134 int propertyIndex = metaObject()->indexOfProperty(propname); 0135 QMetaProperty metaProperty = metaObject()->property(propertyIndex); 0136 return QVariant(metaProperty.metaType(), propertyPtr); 0137 } 0138 0139 if (m_sync) { 0140 return property(propname); 0141 } else { 0142 if (!isValid()) { 0143 QString errorMessage = QStringLiteral("This Extended DBus interface is not valid yet."); 0144 m_lastExtendedError = QDBusMessage::createError(QDBusError::Failed, errorMessage); 0145 qDebug() << Q_FUNC_INFO << errorMessage; 0146 return QVariant(); 0147 } 0148 0149 int propertyIndex = metaObject()->indexOfProperty(propname); 0150 0151 if (-1 == propertyIndex) { 0152 QString errorMessage = QStringLiteral("Got unknown property \"%1\" to read") 0153 .arg(QString::fromLatin1(propname)); 0154 m_lastExtendedError = QDBusMessage::createError(QDBusError::Failed, errorMessage); 0155 qWarning() << Q_FUNC_INFO << errorMessage; 0156 return QVariant(); 0157 } 0158 0159 QMetaProperty metaProperty = metaObject()->property(propertyIndex); 0160 0161 if (!metaProperty.isReadable()) { 0162 QString errorMessage = QStringLiteral("Property \"%1\" is NOT readable") 0163 .arg(QString::fromLatin1(propname)); 0164 m_lastExtendedError = QDBusMessage::createError(QDBusError::Failed, errorMessage); 0165 qWarning() << Q_FUNC_INFO << errorMessage; 0166 return QVariant(); 0167 } 0168 0169 // is this metatype registered? 0170 const char *expectedSignature = ""; 0171 if (metaProperty.metaType() != QMetaType(QMetaType::QVariant)) { 0172 expectedSignature = QDBusMetaType::typeToSignature(metaProperty.metaType()); 0173 if (0 == expectedSignature) { 0174 QString errorMessage = 0175 QStringLiteral("Type %1 must be registered with Qt D-Bus " 0176 "before it can be used to read property " 0177 "%2.%3") 0178 .arg(metaProperty.typeName(), 0179 interface(), 0180 propname); 0181 m_lastExtendedError = QDBusMessage::createError(QDBusError::Failed, errorMessage); 0182 qWarning() << Q_FUNC_INFO << errorMessage; 0183 return QVariant(); 0184 } 0185 } 0186 0187 asyncProperty(propname); 0188 return QVariant(metaProperty.metaType(), propertyPtr); 0189 } 0190 } 0191 0192 void DBusExtendedAbstractInterface::internalPropSet(const char *propname, const QVariant &value, void *propertyPtr) 0193 { 0194 m_lastExtendedError = QDBusError(); 0195 0196 if (m_sync) { 0197 setProperty(propname, value); 0198 } else { 0199 if (!isValid()) { 0200 QString errorMessage = QStringLiteral("This interface is not yet valid"); 0201 m_lastExtendedError = QDBusMessage::createError(QDBusError::Failed, errorMessage); 0202 qDebug() << Q_FUNC_INFO << errorMessage; 0203 return; 0204 } 0205 0206 int propertyIndex = metaObject()->indexOfProperty(propname); 0207 0208 if (-1 == propertyIndex) { 0209 QString errorMessage = QStringLiteral("Got unknown property \"%1\" to write") 0210 .arg(QString::fromLatin1(propname)); 0211 m_lastExtendedError = QDBusMessage::createError(QDBusError::Failed, errorMessage); 0212 qWarning() << Q_FUNC_INFO << errorMessage; 0213 return; 0214 } 0215 0216 QMetaProperty metaProperty = metaObject()->property(propertyIndex); 0217 0218 if (!metaProperty.isWritable()) { 0219 QString errorMessage = QStringLiteral("Property \"%1\" is NOT writable") 0220 .arg(QString::fromLatin1(propname)); 0221 m_lastExtendedError = QDBusMessage::createError(QDBusError::Failed, errorMessage); 0222 qWarning() << Q_FUNC_INFO << errorMessage; 0223 return; 0224 } 0225 0226 asyncSetProperty(propname, QVariant(metaProperty.metaType(), propertyPtr)); 0227 } 0228 } 0229 0230 QVariant DBusExtendedAbstractInterface::asyncProperty(const QString &propertyName) 0231 { 0232 QDBusMessage msg = QDBusMessage::createMethodCall(service(), path(), *dBusPropertiesInterface(), QStringLiteral("Get")); 0233 msg << interface() << propertyName; 0234 QDBusPendingReply<QVariant> async = connection().asyncCall(msg); 0235 DBusExtendedPendingCallWatcher *watcher = new DBusExtendedPendingCallWatcher(async, propertyName, QVariant(), this); 0236 0237 connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(onAsyncPropertyFinished(QDBusPendingCallWatcher*))); 0238 0239 return QVariant(); 0240 } 0241 0242 void DBusExtendedAbstractInterface::asyncSetProperty(const QString &propertyName, const QVariant &value) 0243 { 0244 QDBusMessage msg = QDBusMessage::createMethodCall(service(), path(), *dBusPropertiesInterface(), QStringLiteral("Set")); 0245 msg << interface() << propertyName << value; 0246 QDBusPendingReply<QVariant> async = connection().asyncCall(msg); 0247 DBusExtendedPendingCallWatcher *watcher = new DBusExtendedPendingCallWatcher(async, propertyName, value, this); 0248 0249 connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(onAsyncSetPropertyFinished(QDBusPendingCallWatcher*))); 0250 } 0251 0252 0253 void DBusExtendedAbstractInterface::onAsyncPropertyFinished(DBusExtendedPendingCallWatcher *watcher) 0254 { 0255 QDBusPendingReply<QVariant> reply = *watcher; 0256 0257 if (reply.isError()) { 0258 m_lastExtendedError = reply.error(); 0259 } else { 0260 int propertyIndex = metaObject()->indexOfProperty(watcher->asyncProperty().toLatin1().constData()); 0261 QVariant value = demarshall(interface(), 0262 metaObject()->property(propertyIndex), 0263 reply.value(), 0264 &m_lastExtendedError); 0265 0266 if (m_lastExtendedError.isValid()) { 0267 emit propertyInvalidated(watcher->asyncProperty()); 0268 } else { 0269 emit propertyChanged(watcher->asyncProperty(), value); 0270 } 0271 } 0272 0273 emit asyncPropertyFinished(watcher->asyncProperty()); 0274 watcher->deleteLater(); 0275 } 0276 0277 void DBusExtendedAbstractInterface::onAsyncSetPropertyFinished(DBusExtendedPendingCallWatcher *watcher) 0278 { 0279 QDBusPendingReply<QVariant> reply = *watcher; 0280 0281 if (reply.isError()) { 0282 m_lastExtendedError = reply.error(); 0283 } else { 0284 m_lastExtendedError = QDBusError(); 0285 } 0286 0287 emit asyncSetPropertyFinished(watcher->asyncProperty()); 0288 0289 // Resetting the property to its previous value after sending the 0290 // finished signal 0291 if (reply.isError()) { 0292 m_lastExtendedError = QDBusError(); 0293 emit propertyChanged(watcher->asyncProperty(), watcher->previousValue()); 0294 } 0295 0296 watcher->deleteLater(); 0297 } 0298 0299 void DBusExtendedAbstractInterface::onAsyncGetAllPropertiesFinished(QDBusPendingCallWatcher *watcher) 0300 { 0301 m_getAllPendingCallWatcher = 0; 0302 0303 QDBusPendingReply<QVariantMap> reply = *watcher; 0304 0305 if (reply.isError()) { 0306 m_lastExtendedError = reply.error(); 0307 } else { 0308 m_lastExtendedError = QDBusError(); 0309 } 0310 0311 emit asyncGetAllPropertiesFinished(); 0312 0313 if (!reply.isError()) { 0314 onPropertiesChanged(interface(), reply.value(), QStringList()); 0315 } 0316 0317 watcher->deleteLater(); 0318 } 0319 0320 void DBusExtendedAbstractInterface::onPropertiesChanged(const QString& interfaceName, 0321 const QVariantMap& changedProperties, 0322 const QStringList& invalidatedProperties) 0323 { 0324 if (interfaceName == interface()) { 0325 QVariantMap::const_iterator i = changedProperties.constBegin(); 0326 while (i != changedProperties.constEnd()) { 0327 int propertyIndex = metaObject()->indexOfProperty(i.key().toLatin1().constData()); 0328 0329 if (-1 == propertyIndex) { 0330 qDebug() << Q_FUNC_INFO << "Got unknown changed property" << i.key(); 0331 } else { 0332 QVariant value = demarshall(interface(), metaObject()->property(propertyIndex), i.value(), &m_lastExtendedError); 0333 0334 if (m_lastExtendedError.isValid()) { 0335 emit propertyInvalidated(i.key()); 0336 } else { 0337 emit propertyChanged(i.key(), value); 0338 } 0339 } 0340 0341 ++i; 0342 } 0343 0344 QStringList::const_iterator j = invalidatedProperties.constBegin(); 0345 while (j != invalidatedProperties.constEnd()) { 0346 if (-1 == metaObject()->indexOfProperty(j->toLatin1().constData())) { 0347 qDebug() << Q_FUNC_INFO << "Got unknown invalidated property" << *j; 0348 } else { 0349 m_lastExtendedError = QDBusError(); 0350 emit propertyInvalidated(*j); 0351 } 0352 0353 ++j; 0354 } 0355 } 0356 } 0357 0358 QVariant DBusExtendedAbstractInterface::demarshall(const QString &interface, const QMetaProperty &metaProperty, const QVariant &value, QDBusError *error) 0359 { 0360 Q_ASSERT(metaProperty.isValid()); 0361 Q_ASSERT(error != 0); 0362 0363 if (value.userType() == metaProperty.userType()) { 0364 // No need demarshalling. Passing back straight away ... 0365 *error = QDBusError(); 0366 return value; 0367 } 0368 0369 QVariant result = QVariant(metaProperty.metaType(), (void *)nullptr); 0370 QString errorMessage; 0371 const char *expectedSignature = QDBusMetaType::typeToSignature(metaProperty.metaType()); 0372 0373 if (value.userType() == qMetaTypeId<QDBusArgument>()) { 0374 // demarshalling a DBus argument ... 0375 QDBusArgument dbusArg = value.value<QDBusArgument>(); 0376 0377 if (expectedSignature == dbusArg.currentSignature().toLatin1()) { 0378 QDBusMetaType::demarshall(dbusArg, metaProperty.metaType(), result.data()); 0379 if (!result.isValid()) { 0380 errorMessage = QStringLiteral("Unexpected failure demarshalling " 0381 "upon PropertiesChanged signal arrival " 0382 "for property `%3.%4' (expected type `%5' (%6))") 0383 .arg(interface, 0384 QString::fromLatin1(metaProperty.name()), 0385 QString::fromLatin1(metaProperty.typeName()), 0386 expectedSignature); 0387 } 0388 } else { 0389 errorMessage = QStringLiteral("Unexpected `user type' (%2) " 0390 "upon PropertiesChanged signal arrival " 0391 "for property `%3.%4' (expected type `%5' (%6))") 0392 .arg(dbusArg.currentSignature(), 0393 interface, 0394 QString::fromLatin1(metaProperty.name()), 0395 QString::fromLatin1(metaProperty.typeName()), 0396 QString::fromLatin1(expectedSignature)); 0397 } 0398 } else { 0399 const char *actualSignature = QDBusMetaType::typeToSignature(value.metaType()); 0400 0401 errorMessage = QStringLiteral("Unexpected `%1' (%2) " 0402 "upon PropertiesChanged signal arrival " 0403 "for property `%3.%4' (expected type `%5' (%6))") 0404 .arg(QString::fromLatin1(value.typeName()), 0405 QString::fromLatin1(actualSignature), 0406 interface, 0407 QString::fromLatin1(metaProperty.name()), 0408 QString::fromLatin1(metaProperty.typeName()), 0409 QString::fromLatin1(expectedSignature)); 0410 } 0411 0412 if (errorMessage.isEmpty()) { 0413 *error = QDBusError(); 0414 } else { 0415 *error = QDBusMessage::createError(QDBusError::InvalidSignature, errorMessage); 0416 qDebug() << Q_FUNC_INFO << errorMessage; 0417 } 0418 0419 return result; 0420 }