1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the plugins 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 The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/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 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file. Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
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
51 namespace Maemo {
52
53 /*!
54 \class Maemo::DBusDispatcher
55
56 \brief DBusDispatcher is a class that can send DBUS method call
57 messages and receive unicast signals from DBUS objects.
58 */
59
60 class DBusDispatcherPrivate
61 {
62 public:
DBusDispatcherPrivate(const QString & service,const QString & path,const QString & interface,const QString & signalPath)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
~DBusDispatcherPrivate()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
constantVariantList(const QVariantList & variantList)90 static 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
variantToSignature(const QVariant & argument,bool constantList=true)106 static 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
appendVariantToDBusMessage(const QVariant & argument,DBusMessageIter * dbus_iter)153 static 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 QByteArray data = argument.toString().toUtf8();
199 str_data = data.data();
200 dbus_message_iter_append_basic(dbus_iter, DBUS_TYPE_STRING,
201 &str_data);
202 break;
203 }
204
205 case QVariant::StringList:
206 str_list = argument.toStringList();
207 dbus_message_iter_open_container(dbus_iter, DBUS_TYPE_ARRAY,
208 "s", &array_iter);
209 for (idx = 0; idx < str_list.size(); idx++) {
210 QByteArray data = str_list.at(idx).toLatin1();
211 str_data = data.data();
212 dbus_message_iter_append_basic(&array_iter,
213 DBUS_TYPE_STRING,
214 &str_data);
215 }
216 dbus_message_iter_close_container(dbus_iter, &array_iter);
217 break;
218
219 case QVariant::UInt:
220 uint32_data = argument.toUInt();
221 dbus_message_iter_append_basic(dbus_iter, DBUS_TYPE_UINT32,
222 &uint32_data);
223 break;
224
225 case QVariant::ULongLong:
226 uint64_data = argument.toULongLong();
227 dbus_message_iter_append_basic(dbus_iter, DBUS_TYPE_UINT64,
228 &uint64_data);
229 break;
230
231 case QVariant::LongLong:
232 int64_data = argument.toLongLong();
233 dbus_message_iter_append_basic(dbus_iter, DBUS_TYPE_INT64,
234 &int64_data);
235 break;
236
237 case QVariant::List:
238 {
239 QVariantList variantList = argument.toList();
240 bool constantList = constantVariantList(variantList);
241 DBusMessageIter array_iter;
242
243 // List is mapped either as an DBUS array (all items same type)
244 // DBUS struct (variable types) depending on constantList
245 if (constantList) {
246 // Resolve the signature for the first item
247 QString signature = "";
248 if (!variantList.isEmpty()) {
249 signature = variantToSignature(
250 variantList[0],
251 constantVariantList(variantList[0].toList()));
252 }
253
254 // Mapped as DBUS array
255 dbus_message_iter_open_container(dbus_iter, DBUS_TYPE_ARRAY,
256 signature.toAscii(),
257 &array_iter);
258
259 foreach(QVariant listItem, variantList) {
260 appendVariantToDBusMessage(listItem, &array_iter);
261 }
262
263 dbus_message_iter_close_container(dbus_iter, &array_iter);
264 } else {
265 // Mapped as DBUS struct
266 dbus_message_iter_open_container(dbus_iter, DBUS_TYPE_STRUCT,
267 NULL,
268 &array_iter);
269
270 foreach(QVariant listItem, variantList) {
271 appendVariantToDBusMessage(listItem, &array_iter);
272 }
273
274 dbus_message_iter_close_container(dbus_iter, &array_iter);
275 }
276
277 break;
278 }
279 default:
280 qDebug() << "Unsupported variant type: " << argument.type();
281 break;
282 }
283
284 return true;
285 }
286
getVariantFromDBusMessage(DBusMessageIter * iter)287 static QVariant getVariantFromDBusMessage(DBusMessageIter *iter) {
288 dbus_bool_t bool_data;
289 dbus_int32_t int32_data;
290 dbus_uint32_t uint32_data;
291 dbus_int64_t int64_data;
292 dbus_uint64_t uint64_data;
293 char *str_data;
294 char char_data;
295 int argtype = dbus_message_iter_get_arg_type(iter);
296
297 switch (argtype) {
298
299 case DBUS_TYPE_BOOLEAN:
300 {
301 dbus_message_iter_get_basic(iter, &bool_data);
302 QVariant variant((bool)bool_data);
303 return variant;
304 }
305
306 case DBUS_TYPE_ARRAY:
307 {
308 // Handle all arrays here
309 int elem_type = dbus_message_iter_get_element_type(iter);
310 DBusMessageIter array_iter;
311
312 dbus_message_iter_recurse(iter, &array_iter);
313
314 if (elem_type == DBUS_TYPE_BYTE) {
315 QByteArray byte_array;
316 do {
317 dbus_message_iter_get_basic(&array_iter, &char_data);
318 byte_array.append(char_data);
319 } while (dbus_message_iter_next(&array_iter));
320 QVariant variant(byte_array);
321 return variant;
322 } else if (elem_type == DBUS_TYPE_STRING) {
323 QStringList str_list;
324 do {
325 dbus_message_iter_get_basic(&array_iter, &str_data);
326 str_list.append(str_data);
327 } while (dbus_message_iter_next(&array_iter));
328 QVariant variant(str_list);
329 return variant;
330 } else {
331 QVariantList variantList;
332 do {
333 variantList << getVariantFromDBusMessage(&array_iter);
334 } while (dbus_message_iter_next(&array_iter));
335 QVariant variant(variantList);
336 return variant;
337 }
338 break;
339 }
340
341 case DBUS_TYPE_BYTE:
342 {
343 dbus_message_iter_get_basic(iter, &char_data);
344 QChar ch(char_data);
345 QVariant variant(ch);
346 return variant;
347 }
348
349 case DBUS_TYPE_INT32:
350 {
351 dbus_message_iter_get_basic(iter, &int32_data);
352 QVariant variant((int)int32_data);
353 return variant;
354 }
355
356 case DBUS_TYPE_UINT32:
357 {
358 dbus_message_iter_get_basic(iter, &uint32_data);
359 QVariant variant((uint)uint32_data);
360 return variant;
361 }
362
363 case DBUS_TYPE_STRING:
364 {
365 dbus_message_iter_get_basic(iter, &str_data);
366 QString str(QString::fromUtf8(str_data));
367 QVariant variant(str);
368 return variant;
369 }
370
371 case DBUS_TYPE_INT64:
372 {
373 dbus_message_iter_get_basic(iter, &int64_data);
374 QVariant variant((qlonglong)int64_data);
375 return variant;
376 }
377
378 case DBUS_TYPE_UINT64:
379 {
380 dbus_message_iter_get_basic(iter, &uint64_data);
381 QVariant variant((qulonglong)uint64_data);
382 return variant;
383 }
384
385 case DBUS_TYPE_STRUCT:
386 {
387 // Handle all structs here
388 DBusMessageIter struct_iter;
389 dbus_message_iter_recurse(iter, &struct_iter);
390
391 QVariantList variantList;
392 do {
393 variantList << getVariantFromDBusMessage(&struct_iter);
394 } while (dbus_message_iter_next(&struct_iter));
395 QVariant variant(variantList);
396 return variant;
397 }
398
399 default:
400 qDebug() << "Unsupported DBUS type: " << argtype;
401 }
402
403 return QVariant();
404 }
405
signalHandler(DBusConnection * connection,DBusMessage * message,void * object_ref)406 static DBusHandlerResult signalHandler (DBusConnection *connection,
407 DBusMessage *message,
408 void *object_ref) {
409 (void)connection;
410 QString interface;
411 QString signal;
412 DBusDispatcher *dispatcher = (DBusDispatcher *)object_ref;
413
414 if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_SIGNAL) {
415 interface = dbus_message_get_interface(message);
416 signal = dbus_message_get_member(message);
417
418 QList<QVariant> arglist;
419 DBusMessageIter dbus_iter;
420
421 if (dbus_message_iter_init(message, &dbus_iter)) {
422 // Read return arguments
423 while (dbus_message_iter_get_arg_type (&dbus_iter) != DBUS_TYPE_INVALID) {
424 arglist << getVariantFromDBusMessage(&dbus_iter);
425 dbus_message_iter_next(&dbus_iter);
426 }
427 }
428
429 dispatcher->emitSignalReceived(interface, signal, arglist);
430 return DBUS_HANDLER_RESULT_HANDLED;
431 }
432 (void)message;
433 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
434 }
435
DBusDispatcher(const QString & service,const QString & path,const QString & interface,QObject * parent)436 DBusDispatcher::DBusDispatcher(const QString& service,
437 const QString& path,
438 const QString& interface,
439 QObject *parent)
440 : QObject(parent),
441 d_ptr(new DBusDispatcherPrivate(service, path, interface, path)) {
442 setupDBus();
443 }
444
DBusDispatcher(const QString & service,const QString & path,const QString & interface,const QString & signalPath,QObject * parent)445 DBusDispatcher::DBusDispatcher(const QString& service,
446 const QString& path,
447 const QString& interface,
448 const QString& signalPath,
449 QObject *parent)
450 : QObject(parent),
451 d_ptr(new DBusDispatcherPrivate(service, path, interface, signalPath)) {
452 setupDBus();
453 }
454
~DBusDispatcher()455 DBusDispatcher::~DBusDispatcher()
456 {
457 if (d_ptr->connection) {
458 dbus_connection_close(d_ptr->connection);
459 dbus_connection_unref(d_ptr->connection);
460 }
461 delete d_ptr;
462 }
463
setupDBus()464 void DBusDispatcher::setupDBus()
465 {
466 d_ptr->connection = dbus_bus_get_private(DBUS_BUS_SYSTEM, NULL);
467
468 if (d_ptr->connection == NULL)
469 qDebug() << "Unable to get DBUS connection!";
470 else {
471 d_ptr->signal_vtable.message_function = signalHandler;
472
473 dbus_connection_set_exit_on_disconnect(d_ptr->connection, FALSE);
474 dbus_connection_setup_with_g_main(d_ptr->connection, g_main_context_get_thread_default());
475 dbus_connection_register_object_path(d_ptr->connection,
476 d_ptr->signalPath.toLatin1(),
477 &d_ptr->signal_vtable,
478 this);
479 }
480 }
481
prepareDBusCall(const QString & service,const QString & path,const QString & interface,const QString & method,const QVariant & arg1=QVariant (),const QVariant & arg2=QVariant (),const QVariant & arg3=QVariant (),const QVariant & arg4=QVariant (),const QVariant & arg5=QVariant (),const QVariant & arg6=QVariant (),const QVariant & arg7=QVariant (),const QVariant & arg8=QVariant ())482 static DBusMessage *prepareDBusCall(const QString& service,
483 const QString& path,
484 const QString& interface,
485 const QString& method,
486 const QVariant& arg1 = QVariant(),
487 const QVariant& arg2 = QVariant(),
488 const QVariant& arg3 = QVariant(),
489 const QVariant& arg4 = QVariant(),
490 const QVariant& arg5 = QVariant(),
491 const QVariant& arg6 = QVariant(),
492 const QVariant& arg7 = QVariant(),
493 const QVariant& arg8 = QVariant())
494 {
495 DBusMessage *message = dbus_message_new_method_call(service.toLatin1(),
496 path.toLatin1(),
497 interface.toLatin1(),
498 method.toLatin1());
499 DBusMessageIter dbus_iter;
500
501 // Append variants to DBUS message
502 QList<QVariant> arglist;
503 if (arg1.isValid()) arglist << arg1;
504 if (arg2.isValid()) arglist << arg2;
505 if (arg3.isValid()) arglist << arg3;
506 if (arg4.isValid()) arglist << arg4;
507 if (arg5.isValid()) arglist << arg5;
508 if (arg6.isValid()) arglist << arg6;
509 if (arg7.isValid()) arglist << arg7;
510 if (arg8.isValid()) arglist << arg8;
511
512 dbus_message_iter_init_append (message, &dbus_iter);
513
514 while (!arglist.isEmpty()) {
515 QVariant argument = arglist.takeFirst();
516 appendVariantToDBusMessage(argument, &dbus_iter);
517 }
518
519 return message;
520 }
521
call(const QString & method,const QVariant & arg1,const QVariant & arg2,const QVariant & arg3,const QVariant & arg4,const QVariant & arg5,const QVariant & arg6,const QVariant & arg7,const QVariant & arg8)522 QList<QVariant> DBusDispatcher::call(const QString& method,
523 const QVariant& arg1,
524 const QVariant& arg2,
525 const QVariant& arg3,
526 const QVariant& arg4,
527 const QVariant& arg5,
528 const QVariant& arg6,
529 const QVariant& arg7,
530 const QVariant& arg8) {
531 DBusMessageIter dbus_iter;
532 DBusMessage *message = prepareDBusCall(d_ptr->service, d_ptr->path,
533 d_ptr->interface, method,
534 arg1, arg2, arg3, arg4, arg5,
535 arg6, arg7, arg8);
536 DBusMessage *reply = dbus_connection_send_with_reply_and_block(
537 d_ptr->connection,
538 message, -1, NULL);
539 dbus_message_unref(message);
540
541 QList<QVariant> replylist;
542 if (reply != NULL && dbus_message_iter_init(reply, &dbus_iter)) {
543 // Read return arguments
544 while (dbus_message_iter_get_arg_type (&dbus_iter) != DBUS_TYPE_INVALID) {
545 replylist << getVariantFromDBusMessage(&dbus_iter);
546 dbus_message_iter_next(&dbus_iter);
547 }
548 }
549 if (reply != NULL) dbus_message_unref(reply);
550 return replylist;
551 }
552
553 class PendingCallInfo {
554 public:
555 QString method;
556 DBusDispatcher *dispatcher;
557 DBusDispatcherPrivate *priv;
558 };
559
freePendingCallInfo(void * memory)560 static void freePendingCallInfo(void *memory) {
561 PendingCallInfo *info = (PendingCallInfo *)memory;
562 delete info;
563 }
564
pendingCallFunction(DBusPendingCall * pending,void * memory)565 static void pendingCallFunction (DBusPendingCall *pending,
566 void *memory) {
567 PendingCallInfo *info = (PendingCallInfo *)memory;
568 QString errorStr;
569 QList<QVariant> replyList;
570 DBusMessage *reply = dbus_pending_call_steal_reply (pending);
571
572 Q_ASSERT(reply != NULL);
573
574 if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
575 errorStr = dbus_message_get_error_name (reply);
576 } else {
577 DBusMessageIter dbus_iter;
578 dbus_message_iter_init(reply, &dbus_iter);
579 // Read return arguments
580 while (dbus_message_iter_get_arg_type (&dbus_iter) != DBUS_TYPE_INVALID) {
581 replyList << getVariantFromDBusMessage(&dbus_iter);
582 dbus_message_iter_next(&dbus_iter);
583 }
584 }
585
586 info->priv->pending_calls.removeOne(pending);
587 info->dispatcher->emitCallReply(info->method, replyList, errorStr);
588 dbus_message_unref(reply);
589 dbus_pending_call_unref(pending);
590 }
591
callAsynchronous(const QString & method,const QVariant & arg1,const QVariant & arg2,const QVariant & arg3,const QVariant & arg4,const QVariant & arg5,const QVariant & arg6,const QVariant & arg7,const QVariant & arg8)592 bool DBusDispatcher::callAsynchronous(const QString& method,
593 const QVariant& arg1,
594 const QVariant& arg2,
595 const QVariant& arg3,
596 const QVariant& arg4,
597 const QVariant& arg5,
598 const QVariant& arg6,
599 const QVariant& arg7,
600 const QVariant& arg8) {
601 DBusMessage *message = prepareDBusCall(d_ptr->service, d_ptr->path,
602 d_ptr->interface, method,
603 arg1, arg2, arg3, arg4, arg5,
604 arg6, arg7, arg8);
605 DBusPendingCall *call = NULL;
606 dbus_bool_t ret = dbus_connection_send_with_reply(d_ptr->connection,
607 message, &call, -1);
608 PendingCallInfo *info = new PendingCallInfo;
609 info->method = method;
610 info->dispatcher = this;
611 info->priv = d_ptr;
612
613 dbus_pending_call_set_notify(call, pendingCallFunction, info, freePendingCallInfo);
614 d_ptr->pending_calls.append(call);
615 return (bool)ret;
616 }
617
emitSignalReceived(const QString & interface,const QString & signal,const QList<QVariant> & args)618 void DBusDispatcher::emitSignalReceived(const QString& interface,
619 const QString& signal,
620 const QList<QVariant>& args) {
621 emit signalReceived(interface, signal, args); }
622
emitCallReply(const QString & method,const QList<QVariant> & args,const QString & error)623 void DBusDispatcher::emitCallReply(const QString& method,
624 const QList<QVariant>& args,
625 const QString& error) {
626 emit callReply(method, args, error); }
627
synchronousDispatch(int timeout_ms)628 void DBusDispatcher::synchronousDispatch(int timeout_ms)
629 {
630 dbus_connection_read_write_dispatch(d_ptr->connection, timeout_ms);
631 }
632
633 } // Maemo namespace
634
635