source: trunk/src/plugins/bearer/icd/dbusdispatcher.cpp

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 22.3 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
6**
7** This file is part of the plugins of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42
43#include <QDebug>
44#include <QtCore>
45#include <poll.h>
46#include <dbus/dbus.h>
47#include <dbus/dbus-glib-lowlevel.h>
48#include <glib.h>
49#include "dbusdispatcher.h"
50
51namespace Maemo {
52
53/*!
54 \class DBusDispatcher
55
56 \brief DBusDispatcher is a class, which is able to send DBUS method call
57 messages and receive unicast signals from DBUS object.
58*/
59
60class DBusDispatcherPrivate
61{
62public:
63 DBusDispatcherPrivate(const QString& service,
64 const QString& path,
65 const QString& interface,
66 const QString& signalPath)
67 : service(service), path(path), interface(interface),
68 signalPath(signalPath), connection(0)
69 {
70 memset(&signal_vtable, 0, sizeof(signal_vtable));
71 }
72
73 ~DBusDispatcherPrivate()
74 {
75 foreach(DBusPendingCall *call, pending_calls) {
76 dbus_pending_call_cancel(call);
77 dbus_pending_call_unref(call);
78 }
79 }
80
81 QString service;
82 QString path;
83 QString interface;
84 QString signalPath;
85 struct DBusConnection *connection;
86 QList<DBusPendingCall *> pending_calls;
87 struct DBusObjectPathVTable signal_vtable;
88};
89
90static bool constantVariantList(const QVariantList& variantList) {
91 // Special case, empty list == empty struct
92 if (variantList.isEmpty()) {
93 return false;
94 } else {
95 QVariant::Type type = variantList[0].type();
96 // Iterate items in the list and check if they are same type
97 foreach(QVariant variant, variantList) {
98 if (variant.type() != type) {
99 return false;
100 }
101 }
102 }
103 return true;
104}
105
106static QString variantToSignature(const QVariant& argument,
107 bool constantList = true) {
108 switch (argument.type()) {
109 case QVariant::Bool:
110 return "b";
111 case QVariant::ByteArray:
112 return "ay";
113 case QVariant::Char:
114 return "y";
115 case QVariant::Int:
116 return "i";
117 case QVariant::UInt:
118 return "u";
119 case QVariant::StringList:
120 return "as";
121 case QVariant::String:
122 return "s";
123 case QVariant::LongLong:
124 return "x";
125 case QVariant::ULongLong:
126 return "t";
127 case QVariant::List:
128 {
129 QString signature;
130 QVariantList variantList = argument.toList();
131 if (!constantList) {
132 signature += DBUS_STRUCT_BEGIN_CHAR_AS_STRING;
133 foreach(QVariant listItem, variantList) {
134 signature += variantToSignature(listItem);
135 }
136 signature += DBUS_STRUCT_END_CHAR_AS_STRING;
137 } else {
138 if (variantList.isEmpty())
139 return "";
140 signature = "a" + variantToSignature(variantList[0]);
141 }
142
143 return signature;
144 }
145 default:
146 qDebug() << "Unsupported variant type: " << argument.type();
147 break;
148 }
149
150 return "";
151}
152
153static bool appendVariantToDBusMessage(const QVariant& argument,
154 DBusMessageIter *dbus_iter) {
155 int idx = 0;
156 DBusMessageIter array_iter;
157 QStringList str_list;
158 dbus_bool_t bool_data;
159 dbus_int32_t int32_data;
160 dbus_uint32_t uint32_data;
161 dbus_int64_t int64_data;
162 dbus_uint64_t uint64_data;
163 char *str_data;
164 char char_data;
165
166 switch (argument.type()) {
167
168 case QVariant::Bool:
169 bool_data = argument.toBool();
170 dbus_message_iter_append_basic(dbus_iter, DBUS_TYPE_BOOLEAN,
171 &bool_data);
172 break;
173
174 case QVariant::ByteArray:
175 str_data = argument.toByteArray().data();
176 dbus_message_iter_open_container(dbus_iter, DBUS_TYPE_ARRAY,
177 DBUS_TYPE_BYTE_AS_STRING, &array_iter);
178 dbus_message_iter_append_fixed_array(&array_iter,
179 DBUS_TYPE_BYTE,
180 &str_data,
181 argument.toByteArray().size());
182 dbus_message_iter_close_container(dbus_iter, &array_iter);
183 break;
184
185 case QVariant::Char:
186 char_data = argument.toChar().toAscii();
187 dbus_message_iter_append_basic(dbus_iter, DBUS_TYPE_BYTE,
188 &char_data);
189 break;
190
191 case QVariant::Int:
192 int32_data = argument.toInt();
193 dbus_message_iter_append_basic(dbus_iter, DBUS_TYPE_INT32,
194 &int32_data);
195 break;
196
197 case QVariant::String:
198 str_data = argument.toString().toLatin1().data();
199 dbus_message_iter_append_basic(dbus_iter, DBUS_TYPE_STRING,
200 &str_data);
201 break;
202
203 case QVariant::StringList:
204 str_list = argument.toStringList();
205 dbus_message_iter_open_container(dbus_iter, DBUS_TYPE_ARRAY,
206 "s", &array_iter);
207 for (idx = 0; idx < str_list.size(); idx++) {
208 str_data = str_list.at(idx).toLatin1().data();
209 dbus_message_iter_append_basic(&array_iter,
210 DBUS_TYPE_STRING,
211 &str_data);
212 }
213 dbus_message_iter_close_container(dbus_iter, &array_iter);
214 break;
215
216 case QVariant::UInt:
217 uint32_data = argument.toUInt();
218 dbus_message_iter_append_basic(dbus_iter, DBUS_TYPE_UINT32,
219 &uint32_data);
220 break;
221
222 case QVariant::ULongLong:
223 uint64_data = argument.toULongLong();
224 dbus_message_iter_append_basic(dbus_iter, DBUS_TYPE_UINT64,
225 &uint64_data);
226 break;
227
228 case QVariant::LongLong:
229 int64_data = argument.toLongLong();
230 dbus_message_iter_append_basic(dbus_iter, DBUS_TYPE_INT64,
231 &int64_data);
232 break;
233
234 case QVariant::List:
235 {
236 QVariantList variantList = argument.toList();
237 bool constantList = constantVariantList(variantList);
238 DBusMessageIter array_iter;
239
240 // List is mapped either as an DBUS array (all items same type)
241 // DBUS struct (variable types) depending on constantList
242 if (constantList) {
243 // Resolve the signature for the first item
244 QString signature = "";
245 if (!variantList.isEmpty()) {
246 signature = variantToSignature(
247 variantList[0],
248 constantVariantList(variantList[0].toList()));
249 }
250
251 // Mapped as DBUS array
252 dbus_message_iter_open_container(dbus_iter, DBUS_TYPE_ARRAY,
253 signature.toAscii(),
254 &array_iter);
255
256 foreach(QVariant listItem, variantList) {
257 appendVariantToDBusMessage(listItem, &array_iter);
258 }
259
260 dbus_message_iter_close_container(dbus_iter, &array_iter);
261 } else {
262 // Mapped as DBUS struct
263 dbus_message_iter_open_container(dbus_iter, DBUS_TYPE_STRUCT,
264 NULL,
265 &array_iter);
266
267 foreach(QVariant listItem, variantList) {
268 appendVariantToDBusMessage(listItem, &array_iter);
269 }
270
271 dbus_message_iter_close_container(dbus_iter, &array_iter);
272 }
273
274 break;
275 }
276 default:
277 qDebug() << "Unsupported variant type: " << argument.type();
278 break;
279 }
280
281 return true;
282}
283
284static QVariant getVariantFromDBusMessage(DBusMessageIter *iter) {
285 dbus_bool_t bool_data;
286 dbus_int32_t int32_data;
287 dbus_uint32_t uint32_data;
288 dbus_int64_t int64_data;
289 dbus_uint64_t uint64_data;
290 char *str_data;
291 char char_data;
292 int argtype = dbus_message_iter_get_arg_type(iter);
293
294 switch (argtype) {
295
296 case DBUS_TYPE_BOOLEAN:
297 {
298 dbus_message_iter_get_basic(iter, &bool_data);
299 QVariant variant((bool)bool_data);
300 return variant;
301 }
302
303 case DBUS_TYPE_ARRAY:
304 {
305 // Handle all arrays here
306 int elem_type = dbus_message_iter_get_element_type(iter);
307 DBusMessageIter array_iter;
308
309 dbus_message_iter_recurse(iter, &array_iter);
310
311 if (elem_type == DBUS_TYPE_BYTE) {
312 QByteArray byte_array;
313 do {
314 dbus_message_iter_get_basic(&array_iter, &char_data);
315 byte_array.append(char_data);
316 } while (dbus_message_iter_next(&array_iter));
317 QVariant variant(byte_array);
318 return variant;
319 } else if (elem_type == DBUS_TYPE_STRING) {
320 QStringList str_list;
321 do {
322 dbus_message_iter_get_basic(&array_iter, &str_data);
323 str_list.append(str_data);
324 } while (dbus_message_iter_next(&array_iter));
325 QVariant variant(str_list);
326 return variant;
327 } else {
328 QVariantList variantList;
329 do {
330 variantList << getVariantFromDBusMessage(&array_iter);
331 } while (dbus_message_iter_next(&array_iter));
332 QVariant variant(variantList);
333 return variant;
334 }
335 break;
336 }
337
338 case DBUS_TYPE_BYTE:
339 {
340 dbus_message_iter_get_basic(iter, &char_data);
341 QChar ch(char_data);
342 QVariant variant(ch);
343 return variant;
344 }
345
346 case DBUS_TYPE_INT32:
347 {
348 dbus_message_iter_get_basic(iter, &int32_data);
349 QVariant variant((int)int32_data);
350 return variant;
351 }
352
353 case DBUS_TYPE_UINT32:
354 {
355 dbus_message_iter_get_basic(iter, &uint32_data);
356 QVariant variant((uint)uint32_data);
357 return variant;
358 }
359
360 case DBUS_TYPE_STRING:
361 {
362 dbus_message_iter_get_basic(iter, &str_data);
363 QString str(str_data);
364 QVariant variant(str);
365 return variant;
366 }
367
368 case DBUS_TYPE_INT64:
369 {
370 dbus_message_iter_get_basic(iter, &int64_data);
371 QVariant variant((qlonglong)int64_data);
372 return variant;
373 }
374
375 case DBUS_TYPE_UINT64:
376 {
377 dbus_message_iter_get_basic(iter, &uint64_data);
378 QVariant variant((qulonglong)uint64_data);
379 return variant;
380 }
381
382 case DBUS_TYPE_STRUCT:
383 {
384 // Handle all structs here
385 DBusMessageIter struct_iter;
386 dbus_message_iter_recurse(iter, &struct_iter);
387
388 QVariantList variantList;
389 do {
390 variantList << getVariantFromDBusMessage(&struct_iter);
391 } while (dbus_message_iter_next(&struct_iter));
392 QVariant variant(variantList);
393 return variant;
394 }
395
396 default:
397 qDebug() << "Unsupported DBUS type: " << argtype;
398 }
399
400 return QVariant();
401}
402
403static DBusHandlerResult signalHandler (DBusConnection *connection,
404 DBusMessage *message,
405 void *object_ref) {
406 (void)connection;
407 QString interface;
408 QString signal;
409 DBusDispatcher *dispatcher = (DBusDispatcher *)object_ref;
410
411 if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_SIGNAL) {
412 interface = dbus_message_get_interface(message);
413 signal = dbus_message_get_member(message);
414
415 QList<QVariant> arglist;
416 DBusMessageIter dbus_iter;
417
418 if (dbus_message_iter_init(message, &dbus_iter)) {
419 // Read return arguments
420 while (dbus_message_iter_get_arg_type (&dbus_iter) != DBUS_TYPE_INVALID) {
421 arglist << getVariantFromDBusMessage(&dbus_iter);
422 dbus_message_iter_next(&dbus_iter);
423 }
424 }
425
426 dispatcher->emitSignalReceived(interface, signal, arglist);
427 return DBUS_HANDLER_RESULT_HANDLED;
428 }
429 (void)message;
430 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
431}
432
433DBusDispatcher::DBusDispatcher(const QString& service,
434 const QString& path,
435 const QString& interface,
436 QObject *parent)
437 : QObject(parent),
438 d_ptr(new DBusDispatcherPrivate(service, path, interface, path)) {
439 setupDBus();
440}
441
442DBusDispatcher::DBusDispatcher(const QString& service,
443 const QString& path,
444 const QString& interface,
445 const QString& signalPath,
446 QObject *parent)
447 : QObject(parent),
448 d_ptr(new DBusDispatcherPrivate(service, path, interface, signalPath)) {
449 setupDBus();
450}
451
452DBusDispatcher::~DBusDispatcher()
453{
454 if (d_ptr->connection) {
455 dbus_connection_close(d_ptr->connection);
456 dbus_connection_unref(d_ptr->connection);
457 }
458 delete d_ptr;
459}
460
461void DBusDispatcher::setupDBus()
462{
463 d_ptr->connection = dbus_bus_get_private(DBUS_BUS_SYSTEM, NULL);
464
465 if (d_ptr->connection == NULL)
466 qDebug() << "Unable to get DBUS connection!";
467 else {
468 d_ptr->signal_vtable.message_function = signalHandler;
469
470 dbus_connection_set_exit_on_disconnect(d_ptr->connection, FALSE);
471 dbus_connection_setup_with_g_main(d_ptr->connection, NULL);
472 dbus_connection_register_object_path(d_ptr->connection,
473 d_ptr->signalPath.toLatin1(),
474 &d_ptr->signal_vtable,
475 this);
476 }
477}
478
479static DBusMessage *prepareDBusCall(const QString& service,
480 const QString& path,
481 const QString& interface,
482 const QString& method,
483 const QVariant& arg1 = QVariant(),
484 const QVariant& arg2 = QVariant(),
485 const QVariant& arg3 = QVariant(),
486 const QVariant& arg4 = QVariant(),
487 const QVariant& arg5 = QVariant(),
488 const QVariant& arg6 = QVariant(),
489 const QVariant& arg7 = QVariant(),
490 const QVariant& arg8 = QVariant())
491{
492 DBusMessage *message = dbus_message_new_method_call(service.toLatin1(),
493 path.toLatin1(),
494 interface.toLatin1(),
495 method.toLatin1());
496 DBusMessageIter dbus_iter;
497
498 // Append variants to DBUS message
499 QList<QVariant> arglist;
500 if (arg1.isValid()) arglist << arg1;
501 if (arg2.isValid()) arglist << arg2;
502 if (arg3.isValid()) arglist << arg3;
503 if (arg4.isValid()) arglist << arg4;
504 if (arg5.isValid()) arglist << arg5;
505 if (arg6.isValid()) arglist << arg6;
506 if (arg7.isValid()) arglist << arg7;
507 if (arg8.isValid()) arglist << arg8;
508
509 dbus_message_iter_init_append (message, &dbus_iter);
510
511 while (!arglist.isEmpty()) {
512 QVariant argument = arglist.takeFirst();
513 appendVariantToDBusMessage(argument, &dbus_iter);
514 }
515
516 return message;
517}
518
519QList<QVariant> DBusDispatcher::call(const QString& method,
520 const QVariant& arg1,
521 const QVariant& arg2,
522 const QVariant& arg3,
523 const QVariant& arg4,
524 const QVariant& arg5,
525 const QVariant& arg6,
526 const QVariant& arg7,
527 const QVariant& arg8) {
528 DBusMessageIter dbus_iter;
529 DBusMessage *message = prepareDBusCall(d_ptr->service, d_ptr->path,
530 d_ptr->interface, method,
531 arg1, arg2, arg3, arg4, arg5,
532 arg6, arg7, arg8);
533 DBusMessage *reply = dbus_connection_send_with_reply_and_block(
534 d_ptr->connection,
535 message, -1, NULL);
536 dbus_message_unref(message);
537
538 QList<QVariant> replylist;
539 if (reply != NULL && dbus_message_iter_init(reply, &dbus_iter)) {
540 // Read return arguments
541 while (dbus_message_iter_get_arg_type (&dbus_iter) != DBUS_TYPE_INVALID) {
542 replylist << getVariantFromDBusMessage(&dbus_iter);
543 dbus_message_iter_next(&dbus_iter);
544 }
545 }
546 if (reply != NULL) dbus_message_unref(reply);
547 return replylist;
548}
549
550class PendingCallInfo {
551public:
552 QString method;
553 DBusDispatcher *dispatcher;
554 DBusDispatcherPrivate *priv;
555};
556
557static void freePendingCallInfo(void *memory) {
558 PendingCallInfo *info = (PendingCallInfo *)memory;
559 delete info;
560}
561
562static void pendingCallFunction (DBusPendingCall *pending,
563 void *memory) {
564 PendingCallInfo *info = (PendingCallInfo *)memory;
565 QString errorStr;
566 QList<QVariant> replyList;
567 DBusMessage *reply = dbus_pending_call_steal_reply (pending);
568
569 Q_ASSERT(reply != NULL);
570
571 if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
572 errorStr = dbus_message_get_error_name (reply);
573 } else {
574 DBusMessageIter dbus_iter;
575 dbus_message_iter_init(reply, &dbus_iter);
576 // Read return arguments
577 while (dbus_message_iter_get_arg_type (&dbus_iter) != DBUS_TYPE_INVALID) {
578 replyList << getVariantFromDBusMessage(&dbus_iter);
579 dbus_message_iter_next(&dbus_iter);
580 }
581 }
582
583 info->priv->pending_calls.removeOne(pending);
584 info->dispatcher->emitCallReply(info->method, replyList, errorStr);
585 dbus_message_unref(reply);
586 dbus_pending_call_unref(pending);
587}
588
589bool DBusDispatcher::callAsynchronous(const QString& method,
590 const QVariant& arg1,
591 const QVariant& arg2,
592 const QVariant& arg3,
593 const QVariant& arg4,
594 const QVariant& arg5,
595 const QVariant& arg6,
596 const QVariant& arg7,
597 const QVariant& arg8) {
598 DBusMessage *message = prepareDBusCall(d_ptr->service, d_ptr->path,
599 d_ptr->interface, method,
600 arg1, arg2, arg3, arg4, arg5,
601 arg6, arg7, arg8);
602 DBusPendingCall *call = NULL;
603 dbus_bool_t ret = dbus_connection_send_with_reply(d_ptr->connection,
604 message, &call, -1);
605 PendingCallInfo *info = new PendingCallInfo;
606 info->method = method;
607 info->dispatcher = this;
608 info->priv = d_ptr;
609
610 dbus_pending_call_set_notify(call, pendingCallFunction, info, freePendingCallInfo);
611 d_ptr->pending_calls.append(call);
612 return (bool)ret;
613}
614
615void DBusDispatcher::emitSignalReceived(const QString& interface,
616 const QString& signal,
617 const QList<QVariant>& args) {
618 emit signalReceived(interface, signal, args); }
619
620void DBusDispatcher::emitCallReply(const QString& method,
621 const QList<QVariant>& args,
622 const QString& error) {
623 emit callReply(method, args, error); }
624
625void DBusDispatcher::synchronousDispatch(int timeout_ms)
626{
627 dbus_connection_read_write_dispatch(d_ptr->connection, timeout_ms);
628}
629
630} // Maemo namespace
631
Note: See TracBrowser for help on using the repository browser.