1
/****************************************************************************
3
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4
** Contact: http://www.qt-project.org/legal
6
** This file is part of the QtSystems module of the Qt Toolkit.
8
** $QT_BEGIN_LICENSE:LGPL$
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 Digia. For licensing terms and
14
** conditions see http://qt.digia.com/licensing. For further information
15
** use the contact form at http://qt.digia.com/contact-us.
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 as published by the Free Software
20
** Foundation and appearing in the file LICENSE.LGPL included in the
21
** packaging of this file. Please review the following information to
22
** ensure the GNU Lesser General Public License version 2.1 requirements
23
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25
** In addition, as a special exception, Digia gives you certain additional
26
** rights. These rights are described in the Digia Qt LGPL Exception
27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29
** GNU General Public License Usage
30
** Alternatively, this file may be used under the terms of the GNU
31
** General Public License version 3.0 as published by the Free Software
32
** Foundation and appearing in the file LICENSE.GPL included in the
33
** packaging of this file. Please review the following information to
34
** ensure the GNU General Public License version 3.0 requirements will be
35
** met: http://www.gnu.org/copyleft/gpl.html.
40
****************************************************************************/
42
#include "objectendpoint_dbus_p.h"
43
#include "instancemanager_p.h"
44
#include "proxyobject_p.h"
45
#include "qsignalintercepter_p.h"
46
#include <private/qmetaobjectbuilder_p.h>
49
#include <QVarLengthArray>
56
Response() : isFinished(false), result(0)
63
typedef QHash<QUuid, Response*> Replies;
64
Q_GLOBAL_STATIC(Replies, openRequests);
66
class ServiceSignalIntercepter : public QSignalIntercepter
68
//Do not put Q_OBJECT here
70
ServiceSignalIntercepter(QObject* sender, const QByteArray& signal,
71
ObjectEndPoint* parent)
72
: QSignalIntercepter(sender, signal, parent), endPoint(parent)
77
void setMetaIndex(int index)
83
void activated( const QList<QVariant>& args )
85
endPoint->invokeRemote(metaIndex, args, QMetaType::Void);
88
ObjectEndPoint* endPoint;
92
struct ClientInstance {
94
QRemoteServiceRegister::Entry entry;
99
class ObjectEndPointPrivate
102
ObjectEndPointPrivate()
106
~ObjectEndPointPrivate()
110
// Used on client and service side
111
ObjectEndPoint::Type endPointType;
112
ObjectEndPoint* parent;
114
// Used to calculate the registered paths on DBus
115
QRemoteServiceRegister::Entry entry;
116
QUuid serviceInstanceId;
118
// Service side local client ownership list
119
QList<ClientInstance> clientList;
123
Client to service communication only used for establishing an object request since the returned
124
proxy is an interface to that object registered on QtDBus. Client communicates directly to QtDBus
125
for method and property access. Signals are automatically relayed from QtDBus to the proxy object.
127
ObjectEndPoint::ObjectEndPoint(Type type, QServiceIpcEndPoint* comm, QObject* parent)
128
: QObject(parent), dispatch(comm), service(0), iface(0)
131
d = new ObjectEndPointPrivate;
133
d->endPointType = type;
135
dispatch->setParent(this);
136
connect(dispatch, SIGNAL(readyRead()), this, SLOT(newPackageReady()));
137
if (type == Client) {
138
// client waiting for construct proxy and registers DBus custom type
139
qDBusRegisterMetaType<QServiceUserTypeDBus>();
140
qRegisterMetaType<QServiceUserTypeDBus>();
143
connect(InstanceManager::instance(),
144
SIGNAL(instanceClosed(QRemoteServiceRegister::Entry,QUuid)),
145
this, SLOT(unregisterObjectDBus(QRemoteServiceRegister::Entry,QUuid)));
147
if (dispatch->packageAvailable())
148
QTimer::singleShot(0, this, SLOT(newPackageReady()));
152
ObjectEndPoint::~ObjectEndPoint()
160
Removes all instances of the client from the instance manager
162
void ObjectEndPoint::disconnected(const QString& clientId, const QString& instanceId)
165
Q_ASSERT(d->endPointType != ObjectEndPoint::Client);
167
for (int i=d->clientList.size()-1; i>=0; i--) {
168
// Find right client process
169
if (d->clientList[i].clientId == clientId) {
170
if (d->clientList[i].ref-- == 1) {
171
QRemoteServiceRegister::Entry entry = d->clientList[i].entry;
172
QUuid instance = d->clientList[i].instanceId;
174
if (instance.toString() == instanceId) {
175
// Remove an instance from the InstanceManager and local list
176
InstanceManager::instance()->removeObjectInstance(entry, instance);
177
d->clientList.removeAt(i);
185
Unregisters the DBus object
187
void ObjectEndPoint::unregisterObjectDBus(const QRemoteServiceRegister::Entry& entry, const QUuid& id)
189
uint hash = qHash(id.toString());
190
QString objPath = QLatin1Char('/') + entry.interfaceName() + QLatin1Char('/') + entry.version() +
191
QLatin1Char('/') + QString::number(hash);
192
objPath.replace(QLatin1Char('.'), QLatin1Char('/'));
193
QDBusConnection::sessionBus().unregisterObject(objPath, QDBusConnection::UnregisterTree);
197
Client requests proxy object. The proxy is owned by calling
198
code and this object must clean itself up upon destruction of
201
QObject* ObjectEndPoint::constructProxy(const QRemoteServiceRegister::Entry& entry)
204
Q_ASSERT(d->endPointType == ObjectEndPoint::Client);
206
// Request a serialized meta object
208
p.d = new QServicePackagePrivate();
209
p.d->messageId = QUuid::createUuid();
212
Response* response = new Response();
213
openRequests()->insert(p.d->messageId, response);
215
dispatch->writePackage(p);
216
waitForResponse(p.d->messageId);
218
// Get the proxy based on the meta object
219
if (response->isFinished) {
220
if (response->result == 0)
221
qWarning() << "Request for remote service failed";
223
service = reinterpret_cast<QServiceProxy* >(response->result);
225
qDebug() << "response passed but not finished";
228
openRequests()->take(p.d->messageId);
234
// Connect all DBus interface signals to the proxy slots
235
const QMetaObject *mo = service->metaObject();
236
while (mo && strcmp(mo->className(), "QObject")) {
237
for (int i = mo->methodOffset(); i < mo->methodCount(); i++) {
238
const QMetaMethod mm = mo->method(i);
239
if (mm.methodType() == QMetaMethod::Signal) {
240
QByteArray sig(mm.methodSignature());
242
bool customType = false;
244
QList<QByteArray> params = mm.parameterTypes();
245
for (int arg = 0; arg < params.size(); arg++) {
246
const QByteArray& type = params[arg];
247
int variantType = QMetaType::type(type);
248
if (variantType >= QMetaType::User || variantType == QMetaType::QVariant) {
249
sig.replace(QByteArray(type), QByteArray("QDBusVariant"));
254
int serviceIndex = iface->metaObject()->indexOfSignal(sig);
255
QByteArray signal = QByteArray("2").append(sig);
257
if (serviceIndex > 0) {
259
QObject::connect(iface, signal.constData(), signalsObject, signal.constData());
261
ServiceSignalIntercepter *intercept =
262
new ServiceSignalIntercepter((QObject*)signalsObject, signal, this);
263
intercept->setMetaIndex(localToRemote[i]);
265
QObject::connect(iface, signal.constData(), service, signal.constData());
270
mo = mo->superClass();
277
Received a new package from the DBus client-server controller.
278
Once an object request is handled there is only direct communication to the DBus object so
279
no other package types should be received on this layer.
281
void ObjectEndPoint::newPackageReady()
283
// Client and service side
284
while (dispatch->packageAvailable())
286
// must call get getSecurityCredentials everytime you call nextPackage
287
QServiceClientCredentials creds;
288
dispatch->getSecurityCredentials(creds);
289
QServicePackage p = dispatch->nextPackage();
293
if (p.d->packageType == QServicePackage::ObjectCreation) {
294
objectRequest(p, creds);
296
qWarning() << "Unknown package type received.";
301
void ObjectEndPoint::setLookupTable(int *local, int *remote)
303
localToRemote = local;
304
remoteToLocal = remote;
308
Service finds existing objects or spawns new object instances and registers them on DBus using a
309
hash of the unique instance ID. This registered object has a special metaobject representation
310
of the service that is compatible with the QDBus type system.
312
Client receives a package containing the information to connect an interface to the registered
315
void ObjectEndPoint::objectRequest(const QServicePackage& p, QServiceClientCredentials& creds)
317
if (p.d->responseType != QServicePackage::NotAResponse ) {
319
Q_ASSERT(d->endPointType == ObjectEndPoint::Client);
321
d->serviceInstanceId = p.d->instanceId;
322
d->entry = p.d->entry;
324
Response* response = openRequests()->value(p.d->messageId);
325
if (p.d->responseType == QServicePackage::Failed) {
326
response->result = 0;
327
response->isFinished = true;
328
QTimer::singleShot(0, this, SIGNAL(pendingRequestFinished()));
329
qWarning() << "Service instantiation failed";
333
// Deserialize meta object and create proxy object
334
QServiceProxy* proxy = new QServiceProxy(p.d->payload.toByteArray(), this);
335
response->result = reinterpret_cast<void *>(proxy);
336
response->isFinished = true;
338
// Create DBUS interface by using a hash of the service instance ID
339
QString serviceName = QStringLiteral("com.nokia.qtmobility.sfw.") + p.d->entry.serviceName();
340
uint hash = qHash(d->serviceInstanceId.toString());
341
QString objPath = QLatin1Char('/') + p.d->entry.interfaceName() + QLatin1Char('/') + p.d->entry.version() + QLatin1Char('/') + QString::number(hash);
342
objPath.replace(QLatin1Char('.'), QLatin1Char('/'));
345
qDebug() << "Client Interface ObjectPath:" << objPath;
347
// Instantiate our DBus interface and its corresponding signals object
349
iface = new QDBusInterface(serviceName, objPath, QString(), QDBusConnection::sessionBus(), this);
350
signalsObject = new QServiceMetaObjectDBus(iface, true);
352
// Wake up waiting proxy construction code
353
QTimer::singleShot(0, this, SIGNAL(pendingRequestFinished()));
357
Q_ASSERT(d->endPointType == ObjectEndPoint::Service);
359
QServicePackage response = p.createResponse();
360
InstanceManager* iManager = InstanceManager::instance();
362
if (!creds.isValid()) {
363
qWarning() << "SFW Unable to get socket credentials client asking for" << p.d->entry.interfaceName() << p.d->entry.serviceName() << "this may fail in the future";
366
// Instantiate service object from type register
367
service = iManager->createObjectInstance(p.d->entry, d->serviceInstanceId, creds);
369
qWarning() << "Cannot instantiate service object";
370
dispatch->writePackage(response);
374
if (!creds.isClientAccepted()) {
375
qWarning() << "SFW Security failure by" <<
376
creds.getProcessIdentifier() <<
377
creds.getUserIdentifier() <<
378
creds.getGroupIdentifier() <<
379
"requesting" << p.d->entry.interfaceName() << p.d->entry.serviceName();
380
iManager->removeObjectInstance(p.d->entry, d->serviceInstanceId);
381
dispatch->writePackage(response);
385
// Start DBus connection and register proxy service
386
if (!QDBusConnection::sessionBus().isConnected()) {
387
qWarning() << "Cannot connect to DBus";
390
// DBus registration path uses a hash of the service instance ID
391
QString serviceName = QStringLiteral("com.nokia.qtmobility.sfw.") + p.d->entry.serviceName();
392
uint hash = qHash(d->serviceInstanceId.toString());
393
QString objPath = QLatin1Char('/') + p.d->entry.interfaceName() + QLatin1Char('/') + p.d->entry.version() + QLatin1Char('/') + QString::number(hash);
394
objPath.replace(QLatin1Char('.'), QLatin1Char('/'));
396
QServiceMetaObjectDBus *serviceDBus = new QServiceMetaObjectDBus(service);
397
QDBusConnection::sessionBus().registerObject(objPath, serviceDBus, QDBusConnection::ExportAllContents);
399
QString clientId = p.d->payload.toString();
402
for (int i=d->clientList.size()-1; i>=0; i--) {
403
// Find right client process
404
if (d->clientList[i].clientId == clientId) {
405
d->clientList[i].ref++;
412
// Add new instance to client ownership list
414
c.clientId = clientId;
415
c.entry = p.d->entry;
416
c.instanceId = d->serviceInstanceId;
424
qDebug() << "Service Interface ObjectPath:" << objPath;
426
const QMetaObject *s_meta = service->metaObject();
427
qDebug() << "+++++++++++++++++++++SERVICE+++++++++++++++++++++++";
428
qDebug() << s_meta->className();
429
qDebug() << "METHOD COUNT: " << s_meta->methodCount();
430
for (int i=0; i<s_meta->methodCount(); i++) {
431
QMetaMethod mm = s_meta->method(i);
434
switch (mm.methodType()) {
435
case QMetaMethod::Method:
436
type = "Q_INVOKABLE";
438
case QMetaMethod::Signal:
441
case QMetaMethod::Slot:
448
QString returnType = mm.typeName();
449
if (returnType == "") returnType = "void";
451
qDebug() << "METHOD" << type << ":" << returnType << mm.methodSignature();
453
qDebug() << "++++++++++++++++++++++++++++++++++++++++++++++++++++";
455
iface = new QDBusInterface(serviceName, objPath, "", QDBusConnection::sessionBus(), this);
456
const QMetaObject *i_meta = iface->metaObject();
457
qDebug() << "++++++++++++++++++++DBUS SERVICE++++++++++++++++++++";
458
qDebug() << i_meta->className();
459
qDebug() << "METHOD COUNT: " << i_meta->methodCount();
460
for (int i=0; i<i_meta->methodCount(); i++) {
461
QMetaMethod mm = i_meta->method(i);
464
switch (mm.methodType()) {
465
case QMetaMethod::Method:
466
type = "Q_INVOKABLE";
468
case QMetaMethod::Signal:
471
case QMetaMethod::Slot:
478
QString returnType = mm.typeName();
479
if (returnType == "") returnType = "void";
481
qDebug() << "METHOD" << type << ":" << returnType << mm.methodSignature();
483
qDebug() << "+++++++++++++++++++++++++++++++++++++++++++++++++++";
486
// Get meta object from type register
487
const QMetaObject* meta = iManager->metaObject(p.d->entry);
489
qDebug() << "Unknown type" << p.d->entry;
490
dispatch->writePackage(response);
494
// Serialize meta object
496
QDataStream stream( &data, QIODevice::WriteOnly | QIODevice::Append );
497
QMetaObjectBuilder builder(meta);
498
builder.serialize(stream);
500
// Send meta object and instance ID to the client for processing
501
d->entry = p.d->entry;
502
response.d->instanceId = d->serviceInstanceId;
503
response.d->entry = p.d->entry;
504
response.d->responseType = QServicePackage::Success;
505
response.d->payload = QVariant(data);
506
dispatch->writePackage(response);
511
Returns the created service instance Id
513
QString ObjectEndPoint::getInstanceId() const
515
Q_ASSERT(d->endPointType == ObjectEndPoint::Client);
517
return d->serviceInstanceId.toString();
521
Client side property call that directly accesses properties through the DBus interface.
522
Read and reset have special hardcoded DBus methods due to the nature of QtDBus properties
523
without an adaptor class incorrectly forwarding the metacall type
525
QVariant ObjectEndPoint::invokeRemoteProperty(int metaIndex, const QVariant& arg, int /*returnType*/, QMetaObject::Call c )
527
Q_ASSERT(d->endPointType == ObjectEndPoint::Client);
529
const QMetaObject *imeta = service->metaObject();
530
QMetaProperty property = imeta->property(metaIndex);
532
if (c == QMetaObject::WriteProperty) {
533
// Writing property, direct property DBus call
534
if (!iface->setProperty(property.name(), arg)) {
535
qWarning() << "Service property write call failed";
538
} else if (c == QMetaObject::ResetProperty) {
539
// Resetting property, direct special method DBus call
541
args << QVariant(QLatin1String(property.name()));
542
QDBusMessage msg = iface->callWithArgumentList(QDBus::Block, QLatin1String("propertyReset"), args);
543
if (msg.type() == QDBusMessage::InvalidMessage) {
544
qWarning() << "Service property reset call failed";
547
} else if (c == QMetaObject::ReadProperty) {
548
// Reading property, direct special method DBus call
550
args << QVariant(QLatin1String(property.name()));
551
QDBusMessage msg = iface->callWithArgumentList(QDBus::Block, QLatin1String("propertyRead"), args);
552
if (msg.type() == QDBusMessage::ReplyMessage) {
553
QVariantList retList = msg.arguments();
556
qWarning() << "Service property read call failed" << msg.errorMessage();
559
qWarning() << "Invalid property call";
566
Client side method call that converts an argument of type to its corresponding value as a
567
valid type supported by the QtDBus type system.
569
Supports conversion from a QVariant, QList, QMap, QHash, and custom user-defined types.
571
QVariant ObjectEndPoint::toDBusVariant(const QByteArray& typeName, const QVariant& arg)
573
QVariant dbusVariant = arg;
575
int type = QMetaType::type(typeName);
576
if (type == QMetaType::QVariant) {
577
// Wrap QVariants in a QDBusVariant
578
QDBusVariant replacement(arg);
579
dbusVariant = QVariant::fromValue(replacement);
580
} else if (type >= QMetaType::User) {
581
// Wrap custom types in a QDBusVariant of the type name and
582
// a buffer of its variant-wrapped data
584
QDataStream stream(&buffer, QIODevice::ReadWrite | QIODevice::Append);
587
QServiceUserTypeDBus customType;
588
customType.typeName = typeName;
589
customType.variantBuffer = buffer;
591
QDBusVariant replacement(QVariant::fromValue(customType));
592
dbusVariant = QVariant::fromValue(replacement);
599
Client side method call that directly accesses the object through the DBus interface.
600
All arguments and return types are processed and converted accordingly so that all functions
601
satisfy the QtDBus type system.
603
QVariant ObjectEndPoint::invokeRemote(int metaIndex, const QVariantList& args, int returnType)
605
QMetaMethod method = service->metaObject()->method(remoteToLocal[metaIndex]);
607
Q_ASSERT(d->endPointType == ObjectEndPoint::Client);
609
// Check is this is a signal relay
610
if (method.methodType() == QMetaMethod::Signal) {
611
// Convert custom arguments
612
QVariantList convertedList;
613
QList<QByteArray> params = method.parameterTypes();
614
for (int i = 0; i < params.size(); i++) {
615
const QByteArray& type = params[i];
616
int variantType = QMetaType::type(type);
617
if (variantType >= QMetaType::User || variantType == QMetaType::QVariant) {
618
QDBusVariant dbusVariant = qvariant_cast<QDBusVariant>(args[i]);
619
QVariant variant = dbusVariant.variant();
621
if (variantType == QMetaType::QVariant) {
622
convertedList << variant;
624
QByteArray buffer = variant.toByteArray();
625
QDataStream stream(&buffer, QIODevice::ReadWrite);
626
QVariant *customType = new QVariant(variantType, (const void*)0);
627
QMetaType::load(stream, QMetaType::QVariant, customType);
628
convertedList << *customType;
631
convertedList << args[i];
636
const int numArgs = convertedList.size();
637
QVarLengthArray<void *, 32> a( numArgs+1 );
640
const QList<QByteArray> pTypes = method.parameterTypes();
641
for ( int arg = 0; arg < numArgs; ++arg ) {
642
if (pTypes.at(arg) == "QVariant") {
643
a[arg+1] = (void *)&( convertedList[arg] );
645
a[arg+1] = (void *)( convertedList[arg].data() );
649
// Activate the service proxy signal call
650
QMetaObject::activate(service, remoteToLocal[metaIndex], a.data());
654
// Method call so process arguments and convert if not a supported DBus type
655
QVariantList convertedList;
656
QList<QByteArray> params = method.parameterTypes();
657
for (int i = 0; i < params.size(); i++) {
658
QVariant converted = toDBusVariant(params[i], args[i]);
659
convertedList << converted;
662
bool validDBus = false;
665
// Find the method name and try a direct DBus call
666
QString methodName(QLatin1String(method.methodSignature().constData()));
667
methodName.truncate(methodName.indexOf(QLatin1String("(")));
669
if (method.methodType() == QMetaMethod::Slot || method.methodType() == QMetaMethod::Method) {
670
// Slot or Invokable method
671
msg = iface->callWithArgumentList(QDBus::Block, methodName, convertedList);
672
if (msg.type() == QDBusMessage::ReplyMessage) {
677
// DBus call should only fail for methods with invalid type definitions
679
if (returnType == QMetaType::Void) {
684
// Use DBus message return value
685
QVariantList retList = msg.arguments();
688
const QByteArray& retType = QByteArray(method.typeName());
689
int variantType = QMetaType::type(retType);
690
if (variantType == QMetaType::QVariant) {
691
// QVariant return from QDBusVariant wrapper
692
QDBusVariant dbusVariant = qvariant_cast<QDBusVariant>(retList[0]);
693
return dbusVariant.variant();
694
} else if (variantType >= QMetaType::User) {
695
// Custom return type
696
QDBusVariant dbusVariant = qvariant_cast<QDBusVariant>(retList[0]);
697
QVariant convert = dbusVariant.variant();
699
QServiceUserTypeDBus customType = qdbus_cast<QServiceUserTypeDBus>(convert);
700
QByteArray buffer = customType.variantBuffer;
701
QDataStream stream(&buffer, QIODevice::ReadWrite);
703
// Load our buffered variant-wrapped custom return
704
QVariant *customReturn = new QVariant(variantType, (const void*)0);
705
QMetaType::load(stream, QMetaType::QVariant, customReturn);
707
return QVariant(variantType, customReturn->data());
709
// Standard return type
714
qWarning( "%s::%s cannot be called.", service->metaObject()->className(), method.methodSignature().constData());
721
Client side waits for service side requested
723
void ObjectEndPoint::waitForResponse(const QUuid& requestId)
725
Q_ASSERT(d->endPointType == ObjectEndPoint::Client);
727
if (openRequests()->contains(requestId) ) {
728
Response* response = openRequests()->value(requestId);
729
QEventLoop* loop = new QEventLoop( this );
730
connect(this, SIGNAL(pendingRequestFinished()), loop, SLOT(quit()));
732
while (!response->isFinished) {
740
#include "moc_objectendpoint_dbus_p.cpp"