~ubuntu-branches/ubuntu/utopic/qtsystems-opensource-src/utopic-proposed

« back to all changes in this revision

Viewing changes to src/serviceframework/ipc/objectendpoint_dbus.cpp

  • Committer: Package Import Robot
  • Author(s): Timo Jyrinki
  • Date: 2013-06-14 10:32:13 UTC
  • Revision ID: package-import@ubuntu.com-20130614103213-yao8gdav28kntvvj
Tags: upstream-5.0~git20130614
ImportĀ upstreamĀ versionĀ 5.0~git20130614

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/****************************************************************************
 
2
**
 
3
** Copyright (C) 2012 Digia Plc 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: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.
 
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 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.
 
24
**
 
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.
 
28
**
 
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.
 
36
**
 
37
**
 
38
** $QT_END_LICENSE$
 
39
**
 
40
****************************************************************************/
 
41
 
 
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>
 
47
#include <QTimer>
 
48
#include <QEventLoop>
 
49
#include <QVarLengthArray>
 
50
 
 
51
QT_BEGIN_NAMESPACE
 
52
 
 
53
class Response
 
54
{
 
55
public:
 
56
    Response() : isFinished(false), result(0)
 
57
    { }
 
58
 
 
59
    bool isFinished;
 
60
    void* result;
 
61
};
 
62
 
 
63
typedef QHash<QUuid, Response*> Replies;
 
64
Q_GLOBAL_STATIC(Replies, openRequests);
 
65
 
 
66
class ServiceSignalIntercepter : public QSignalIntercepter
 
67
{
 
68
    //Do not put Q_OBJECT here
 
69
public:
 
70
    ServiceSignalIntercepter(QObject* sender, const QByteArray& signal,
 
71
            ObjectEndPoint* parent)
 
72
        : QSignalIntercepter(sender, signal, parent), endPoint(parent)
 
73
    {
 
74
 
 
75
    }
 
76
 
 
77
    void setMetaIndex(int index)
 
78
    {
 
79
        metaIndex = index;
 
80
    }
 
81
 
 
82
protected:
 
83
    void activated( const QList<QVariant>& args )
 
84
    {
 
85
        endPoint->invokeRemote(metaIndex, args, QMetaType::Void);
 
86
    }
 
87
private:
 
88
    ObjectEndPoint* endPoint;
 
89
    int metaIndex;
 
90
};
 
91
 
 
92
struct ClientInstance {
 
93
    QString clientId;
 
94
    QRemoteServiceRegister::Entry entry;
 
95
    QUuid instanceId;
 
96
    int ref;
 
97
};
 
98
 
 
99
class ObjectEndPointPrivate
 
100
{
 
101
public:
 
102
    ObjectEndPointPrivate()
 
103
    {
 
104
    }
 
105
 
 
106
    ~ObjectEndPointPrivate()
 
107
    {
 
108
    }
 
109
 
 
110
    // Used on client and service side
 
111
    ObjectEndPoint::Type endPointType;
 
112
    ObjectEndPoint* parent;
 
113
 
 
114
    // Used to calculate the registered paths on DBus
 
115
    QRemoteServiceRegister::Entry entry;
 
116
    QUuid serviceInstanceId;
 
117
 
 
118
    // Service side local client ownership list
 
119
    QList<ClientInstance> clientList;
 
120
};
 
121
 
 
122
/*!
 
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.
 
126
*/
 
127
ObjectEndPoint::ObjectEndPoint(Type type, QServiceIpcEndPoint* comm, QObject* parent)
 
128
    : QObject(parent), dispatch(comm), service(0), iface(0)
 
129
{
 
130
    Q_ASSERT(dispatch);
 
131
    d = new ObjectEndPointPrivate;
 
132
    d->parent = this;
 
133
    d->endPointType = type;
 
134
 
 
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>();
 
141
        return;
 
142
    } else {
 
143
        connect(InstanceManager::instance(),
 
144
                SIGNAL(instanceClosed(QRemoteServiceRegister::Entry,QUuid)),
 
145
                this, SLOT(unregisterObjectDBus(QRemoteServiceRegister::Entry,QUuid)));
 
146
 
 
147
        if (dispatch->packageAvailable())
 
148
            QTimer::singleShot(0, this, SLOT(newPackageReady()));
 
149
    }
 
150
}
 
151
 
 
152
ObjectEndPoint::~ObjectEndPoint()
 
153
{
 
154
    if (iface)
 
155
       delete  iface;
 
156
    delete d;
 
157
}
 
158
 
 
159
/*!
 
160
    Removes all instances of the client from the instance manager
 
161
*/
 
162
void ObjectEndPoint::disconnected(const QString& clientId, const QString& instanceId)
 
163
{
 
164
    // Service Side
 
165
    Q_ASSERT(d->endPointType != ObjectEndPoint::Client);
 
166
 
 
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;
 
173
 
 
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);
 
178
                }
 
179
            }
 
180
        }
 
181
    }
 
182
}
 
183
 
 
184
/*!
 
185
    Unregisters the DBus object
 
186
*/
 
187
void ObjectEndPoint::unregisterObjectDBus(const QRemoteServiceRegister::Entry& entry, const QUuid& id)
 
188
{
 
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);
 
194
}
 
195
 
 
196
/*!
 
197
    Client requests proxy object. The proxy is owned by calling
 
198
    code and this object must clean itself up upon destruction of
 
199
    proxy.
 
200
*/
 
201
QObject* ObjectEndPoint::constructProxy(const QRemoteServiceRegister::Entry& entry)
 
202
{
 
203
    // Client side
 
204
    Q_ASSERT(d->endPointType == ObjectEndPoint::Client);
 
205
 
 
206
    // Request a serialized meta object
 
207
    QServicePackage p;
 
208
    p.d = new QServicePackagePrivate();
 
209
    p.d->messageId = QUuid::createUuid();
 
210
    p.d->entry = entry;
 
211
 
 
212
    Response* response = new Response();
 
213
    openRequests()->insert(p.d->messageId, response);
 
214
 
 
215
    dispatch->writePackage(p);
 
216
    waitForResponse(p.d->messageId);
 
217
 
 
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";
 
222
        else
 
223
            service = reinterpret_cast<QServiceProxy* >(response->result);
 
224
    } else {
 
225
        qDebug() << "response passed but not finished";
 
226
    }
 
227
 
 
228
    openRequests()->take(p.d->messageId);
 
229
    delete response;
 
230
 
 
231
    if (!service)
 
232
        return 0;
 
233
 
 
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());
 
241
 
 
242
                bool customType = false;
 
243
 
 
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"));
 
250
                        customType = true;
 
251
                    }
 
252
                }
 
253
 
 
254
                int serviceIndex = iface->metaObject()->indexOfSignal(sig);
 
255
                QByteArray signal = QByteArray("2").append(sig);
 
256
 
 
257
                if (serviceIndex > 0) {
 
258
                    if (customType) {
 
259
                        QObject::connect(iface, signal.constData(), signalsObject, signal.constData());
 
260
 
 
261
                        ServiceSignalIntercepter *intercept =
 
262
                            new ServiceSignalIntercepter((QObject*)signalsObject, signal, this);
 
263
                        intercept->setMetaIndex(localToRemote[i]);
 
264
                    } else {
 
265
                        QObject::connect(iface, signal.constData(), service, signal.constData());
 
266
                    }
 
267
                }
 
268
            }
 
269
        }
 
270
        mo = mo->superClass();
 
271
    }
 
272
 
 
273
    return service;
 
274
}
 
275
 
 
276
/*!
 
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.
 
280
*/
 
281
void ObjectEndPoint::newPackageReady()
 
282
{
 
283
    // Client and service side
 
284
    while (dispatch->packageAvailable())
 
285
    {
 
286
        // must call get getSecurityCredentials everytime you call nextPackage
 
287
        QServiceClientCredentials creds;
 
288
        dispatch->getSecurityCredentials(creds);
 
289
        QServicePackage p = dispatch->nextPackage();
 
290
        if (!p.isValid())
 
291
            continue;
 
292
 
 
293
        if (p.d->packageType == QServicePackage::ObjectCreation) {
 
294
            objectRequest(p, creds);
 
295
        } else {
 
296
            qWarning() << "Unknown package type received.";
 
297
        }
 
298
    }
 
299
}
 
300
 
 
301
void ObjectEndPoint::setLookupTable(int *local, int *remote)
 
302
{
 
303
    localToRemote = local;
 
304
    remoteToLocal = remote;
 
305
}
 
306
 
 
307
/*!
 
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.
 
311
 
 
312
    Client receives a package containing the information to connect an interface to the registered
 
313
    DBus object.
 
314
*/
 
315
void ObjectEndPoint::objectRequest(const QServicePackage& p, QServiceClientCredentials& creds)
 
316
{
 
317
    if (p.d->responseType != QServicePackage::NotAResponse ) {
 
318
        // Client side
 
319
        Q_ASSERT(d->endPointType == ObjectEndPoint::Client);
 
320
 
 
321
        d->serviceInstanceId = p.d->instanceId;
 
322
        d->entry = p.d->entry;
 
323
 
 
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";
 
330
            return;
 
331
        }
 
332
 
 
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;
 
337
 
 
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('/'));
 
343
 
 
344
#ifdef DEBUG
 
345
        qDebug() << "Client Interface ObjectPath:" << objPath;
 
346
#endif
 
347
        // Instantiate our DBus interface and its corresponding signals object
 
348
        if (!iface)
 
349
            iface = new QDBusInterface(serviceName, objPath, QString(), QDBusConnection::sessionBus(), this);
 
350
        signalsObject = new QServiceMetaObjectDBus(iface, true);
 
351
 
 
352
        // Wake up waiting proxy construction code
 
353
        QTimer::singleShot(0, this, SIGNAL(pendingRequestFinished()));
 
354
 
 
355
    } else {
 
356
        // Service side
 
357
        Q_ASSERT(d->endPointType == ObjectEndPoint::Service);
 
358
 
 
359
        QServicePackage response = p.createResponse();
 
360
        InstanceManager* iManager = InstanceManager::instance();
 
361
 
 
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";
 
364
        }
 
365
 
 
366
        // Instantiate service object from type register
 
367
        service = iManager->createObjectInstance(p.d->entry, d->serviceInstanceId, creds);
 
368
        if (!service) {
 
369
            qWarning() << "Cannot instantiate service object";
 
370
            dispatch->writePackage(response);
 
371
            return;
 
372
        }
 
373
 
 
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);
 
382
            return;
 
383
        }
 
384
 
 
385
        // Start DBus connection and register proxy service
 
386
        if (!QDBusConnection::sessionBus().isConnected()) {
 
387
            qWarning() << "Cannot connect to DBus";
 
388
        }
 
389
 
 
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('/'));
 
395
 
 
396
        QServiceMetaObjectDBus *serviceDBus = new QServiceMetaObjectDBus(service);
 
397
        QDBusConnection::sessionBus().registerObject(objPath, serviceDBus, QDBusConnection::ExportAllContents);
 
398
 
 
399
        QString clientId = p.d->payload.toString();
 
400
 
 
401
        int exists = 0;
 
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++;
 
406
                exists = 1;
 
407
                break;
 
408
            }
 
409
        }
 
410
 
 
411
        if (!exists) {
 
412
            // Add new instance to client ownership list
 
413
            ClientInstance c;
 
414
            c.clientId = clientId;
 
415
            c.entry = p.d->entry;
 
416
            c.instanceId = d->serviceInstanceId;
 
417
            c.ref = 1;
 
418
            d->clientList << c;
 
419
        }
 
420
 
 
421
 
 
422
 
 
423
#ifdef DEBUG
 
424
        qDebug() << "Service Interface ObjectPath:" << objPath;
 
425
 
 
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);
 
432
 
 
433
            QString type;
 
434
            switch (mm.methodType()) {
 
435
                case QMetaMethod::Method:
 
436
                    type = "Q_INVOKABLE";
 
437
                    break;
 
438
                case QMetaMethod::Signal:
 
439
                    type = "SIGNAL";
 
440
                    break;
 
441
                case QMetaMethod::Slot:
 
442
                    type = "SLOT";
 
443
                    break;
 
444
                default:
 
445
                    break;
 
446
            }
 
447
 
 
448
            QString returnType = mm.typeName();
 
449
            if (returnType == "") returnType = "void";
 
450
 
 
451
            qDebug() << "METHOD" << type << ":" << returnType << mm.methodSignature();
 
452
        }
 
453
        qDebug() << "++++++++++++++++++++++++++++++++++++++++++++++++++++";
 
454
        if (!iface)
 
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);
 
462
 
 
463
            QString type;
 
464
            switch (mm.methodType()) {
 
465
                case QMetaMethod::Method:
 
466
                    type = "Q_INVOKABLE";
 
467
                    break;
 
468
                case QMetaMethod::Signal:
 
469
                    type = "SIGNAL";
 
470
                    break;
 
471
                case QMetaMethod::Slot:
 
472
                    type = "SLOT";
 
473
                    break;
 
474
                default:
 
475
                    break;
 
476
            }
 
477
 
 
478
            QString returnType = mm.typeName();
 
479
            if (returnType == "") returnType = "void";
 
480
 
 
481
            qDebug() << "METHOD" << type << ":" << returnType << mm.methodSignature();
 
482
        }
 
483
        qDebug() << "+++++++++++++++++++++++++++++++++++++++++++++++++++";
 
484
#endif
 
485
 
 
486
        // Get meta object from type register
 
487
        const QMetaObject* meta = iManager->metaObject(p.d->entry);
 
488
        if (!meta) {
 
489
            qDebug() << "Unknown type" << p.d->entry;
 
490
            dispatch->writePackage(response);
 
491
            return;
 
492
        }
 
493
 
 
494
        // Serialize meta object
 
495
        QByteArray data;
 
496
        QDataStream stream( &data, QIODevice::WriteOnly | QIODevice::Append );
 
497
        QMetaObjectBuilder builder(meta);
 
498
        builder.serialize(stream);
 
499
 
 
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);
 
507
    }
 
508
}
 
509
 
 
510
/*!
 
511
    Returns the created service instance Id
 
512
*/
 
513
QString ObjectEndPoint::getInstanceId() const
 
514
{
 
515
    Q_ASSERT(d->endPointType == ObjectEndPoint::Client);
 
516
 
 
517
    return d->serviceInstanceId.toString();
 
518
}
 
519
 
 
520
/*!
 
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
 
524
*/
 
525
QVariant ObjectEndPoint::invokeRemoteProperty(int metaIndex, const QVariant& arg, int /*returnType*/, QMetaObject::Call c )
 
526
{
 
527
    Q_ASSERT(d->endPointType == ObjectEndPoint::Client);
 
528
 
 
529
    const QMetaObject *imeta = service->metaObject();
 
530
    QMetaProperty property = imeta->property(metaIndex);
 
531
 
 
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";
 
536
        }
 
537
 
 
538
    } else if (c == QMetaObject::ResetProperty) {
 
539
        // Resetting property, direct special method DBus call
 
540
        QVariantList args;
 
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";
 
545
        }
 
546
 
 
547
    } else if (c == QMetaObject::ReadProperty) {
 
548
        // Reading property, direct special method DBus call
 
549
        QVariantList args;
 
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();
 
554
            return retList[0];
 
555
        } else {
 
556
            qWarning() << "Service property read call failed" << msg.errorMessage();
 
557
        }
 
558
    } else {
 
559
        qWarning() << "Invalid property call";
 
560
    }
 
561
 
 
562
    return QVariant();
 
563
}
 
564
 
 
565
/*!
 
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.
 
568
 
 
569
    Supports conversion from a QVariant, QList, QMap, QHash, and custom user-defined types.
 
570
*/
 
571
QVariant ObjectEndPoint::toDBusVariant(const QByteArray& typeName, const QVariant& arg)
 
572
{
 
573
    QVariant dbusVariant = arg;
 
574
 
 
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
 
583
        QByteArray buffer;
 
584
        QDataStream stream(&buffer, QIODevice::ReadWrite | QIODevice::Append);
 
585
        stream << arg;
 
586
 
 
587
        QServiceUserTypeDBus customType;
 
588
        customType.typeName = typeName;
 
589
        customType.variantBuffer = buffer;
 
590
 
 
591
        QDBusVariant replacement(QVariant::fromValue(customType));
 
592
        dbusVariant = QVariant::fromValue(replacement);
 
593
    }
 
594
 
 
595
    return dbusVariant;
 
596
}
 
597
 
 
598
/*!
 
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.
 
602
*/
 
603
QVariant ObjectEndPoint::invokeRemote(int metaIndex, const QVariantList& args, int returnType)
 
604
{
 
605
    QMetaMethod method = service->metaObject()->method(remoteToLocal[metaIndex]);
 
606
 
 
607
    Q_ASSERT(d->endPointType == ObjectEndPoint::Client);
 
608
 
 
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();
 
620
 
 
621
                if (variantType == QMetaType::QVariant) {
 
622
                    convertedList << variant;
 
623
                } else {
 
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;
 
629
                }
 
630
            } else {
 
631
                convertedList << args[i];
 
632
            }
 
633
        }
 
634
 
 
635
        // Signal relay
 
636
        const int numArgs = convertedList.size();
 
637
        QVarLengthArray<void *, 32> a( numArgs+1 );
 
638
        a[0] = 0;
 
639
 
 
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] );
 
644
            } else {
 
645
                a[arg+1] = (void *)( convertedList[arg].data() );
 
646
            }
 
647
        }
 
648
 
 
649
        // Activate the service proxy signal call
 
650
        QMetaObject::activate(service, remoteToLocal[metaIndex], a.data());
 
651
        return QVariant();
 
652
    }
 
653
 
 
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;
 
660
    }
 
661
 
 
662
    bool validDBus = false;
 
663
    QDBusMessage msg;
 
664
 
 
665
    // Find the method name and try a direct DBus call
 
666
    QString methodName(QLatin1String(method.methodSignature().constData()));
 
667
    methodName.truncate(methodName.indexOf(QLatin1String("(")));
 
668
 
 
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) {
 
673
            validDBus = true;
 
674
        }
 
675
    }
 
676
 
 
677
    // DBus call should only fail for methods with invalid type definitions
 
678
    if (validDBus) {
 
679
        if (returnType == QMetaType::Void) {
 
680
            // Void method call
 
681
            return QVariant();
 
682
        }
 
683
        else {
 
684
            // Use DBus message return value
 
685
            QVariantList retList = msg.arguments();
 
686
 
 
687
            // Process return
 
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();
 
698
 
 
699
                QServiceUserTypeDBus customType = qdbus_cast<QServiceUserTypeDBus>(convert);
 
700
                QByteArray buffer = customType.variantBuffer;
 
701
                QDataStream stream(&buffer, QIODevice::ReadWrite);
 
702
 
 
703
                // Load our buffered variant-wrapped custom return
 
704
                QVariant *customReturn = new QVariant(variantType, (const void*)0);
 
705
                QMetaType::load(stream, QMetaType::QVariant, customReturn);
 
706
 
 
707
                return QVariant(variantType, customReturn->data());
 
708
            } else {
 
709
                // Standard return type
 
710
                return retList[0];
 
711
            }
 
712
        }
 
713
    } else {
 
714
        qWarning( "%s::%s cannot be called.", service->metaObject()->className(), method.methodSignature().constData());
 
715
    }
 
716
 
 
717
    return QVariant();
 
718
}
 
719
 
 
720
/*!
 
721
    Client side waits for service side requested
 
722
*/
 
723
void ObjectEndPoint::waitForResponse(const QUuid& requestId)
 
724
{
 
725
    Q_ASSERT(d->endPointType == ObjectEndPoint::Client);
 
726
 
 
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()));
 
731
 
 
732
        while (!response->isFinished) {
 
733
            loop->exec();
 
734
        }
 
735
 
 
736
        delete loop;
 
737
    }
 
738
}
 
739
 
 
740
#include "moc_objectendpoint_dbus_p.cpp"
 
741
 
 
742
QT_END_NAMESPACE