* [GAM-35] implement DBus for SESSION or SYSTEM bus
[profile/ivi/audiomanager.git] / AudioManagerDaemon / src / CAmDbusWrapper.cpp
1 /**
2  * Copyright (C) 2012, BMW AG
3  *
4  * This file is part of GENIVI Project AudioManager.
5  *
6  * Contributions are licensed to the GENIVI Alliance under one or more
7  * Contribution License Agreements.
8  *
9  * \copyright
10  * This Source Code Form is subject to the terms of the
11  * Mozilla Public License, v. 2.0. If a  copy of the MPL was not distributed with
12  * this file, You can obtain one at http://mozilla.org/MPL/2.0/.
13  *
14  *
15  * \author Christian Mueller, christian.ei.mueller@bmw.de BMW 2011,2012
16  *
17  * \file CAmDbusWrapper.cpp
18  * For further information see http://www.genivi.org/.
19  *
20  */
21
22 #include "shared/CAmDbusWrapper.h"
23 #include <config.h>
24 #include <fstream>
25 #include <sstream>
26 #include <string>
27 #include <cassert>
28 #include <cstdlib>
29 #include "shared/CAmDltWrapper.h"
30 #include "shared/CAmSocketHandler.h"
31
32 namespace am
33 {
34
35 /**
36  * introspectio header
37  */
38 #define ROOT_INTROSPECT_XML                                                                                             \
39 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                                                               \
40 "<node>"                                                                                                                                \
41 "<interface name='org.AudioManager.freedesktop.DBus.Introspectable'>"   \
42 "<method name='Introspect'>"                                                                                    \
43 "       <arg name='xml_data' type='s' direction='out'/>"                                        \
44 "</method>"                                                                                                                     \
45 "</interface>"                                                                                                                  \
46
47 CAmDbusWrapper* CAmDbusWrapper::mpReference = NULL;
48
49 CAmDbusWrapper::CAmDbusWrapper(CAmSocketHandler* socketHandler, DBusBusType type) :
50         pDbusDispatchCallback(this, &CAmDbusWrapper::dbusDispatchCallback), //
51         pDbusFireCallback(this, &CAmDbusWrapper::dbusFireCallback), //
52         pDbusCheckCallback(this, &CAmDbusWrapper::dbusCheckCallback), //
53         pDbusTimerCallback(this, &CAmDbusWrapper::dbusTimerCallback), //
54         mpDbusConnection(0), //
55         mDBusError(), //
56         mListNodes(), //
57         mpListTimerhandles(), //
58         mpSocketHandler(socketHandler)
59 {
60     assert(mpSocketHandler!=0);
61
62     dbus_error_init(&mDBusError);
63     logInfo("DBusWrapper::DBusWrapper Opening DBus connection");
64     mpDbusConnection = dbus_bus_get(type, &mDBusError);
65     if (dbus_error_is_set(&mDBusError))
66     {
67         logError("DBusWrapper::DBusWrapper Error while getting the DBus");
68         dbus_error_free(&mDBusError);
69     }
70     if (NULL == mpDbusConnection)
71     {
72         logError("DBusWrapper::DBusWrapper DBus Connection is null");
73     }
74
75     //then we need to adopt the dbus to our mainloop:
76     //first, we are old enought to live longer then the connection:
77     dbus_connection_set_exit_on_disconnect(mpDbusConnection, FALSE);
78
79     //we do not need the manual dispatching, since it is not allowed to call from a different thread. So leave it uncommented:
80     //dbus_connection_set_dispatch_status_function
81
82     //add watch functions:
83     dbus_bool_t watch = dbus_connection_set_watch_functions(mpDbusConnection, addWatch, removeWatch, toogleWatch, this, NULL);
84     if (!watch)
85     {
86         logError("DBusWrapper::DBusWrapper Registering of watch functions failed");
87     }
88
89     //add timer functions:
90     dbus_bool_t timer = dbus_connection_set_timeout_functions(mpDbusConnection, addTimeout, removeTimeout, toggleTimeout, this, NULL);
91     if (!timer)
92     {
93         logError("DBusWrapper::DBusWrapper Registering of timer functions failed");
94     }
95
96     //register callback for Introspectio
97     mObjectPathVTable.message_function = CAmDbusWrapper::cbRootIntrospection;
98     dbus_connection_register_object_path(mpDbusConnection, DBUS_SERVICE_OBJECT_PATH, &mObjectPathVTable, this);
99     int ret = dbus_bus_request_name(mpDbusConnection, DBUS_SERVICE_PREFIX, DBUS_NAME_FLAG_DO_NOT_QUEUE, &mDBusError);
100     if (dbus_error_is_set(&mDBusError))
101     {
102         logError("DBusWrapper::DBusWrapper Name Error", mDBusError.message);
103         dbus_error_free(&mDBusError);
104     }
105     if (DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER != ret)
106     {
107         logError("DBusWrapper::DBusWrapper Wrapper is not the Primary Owner ! Another instance already running?", ret);
108         exit(1);
109     }
110 }
111
112 CAmDbusWrapper::~CAmDbusWrapper()
113 {
114     //close the connection again
115     logInfo("DBusWrapper::~DBusWrapper Closing DBus connection");
116     dbus_connection_unref(mpDbusConnection);
117
118     //clean up all timerhandles we created but did not delete before
119     std::vector<sh_timerHandle_t*>::iterator it = mpListTimerhandles.begin();
120     for (; it != mpListTimerhandles.end(); ++it)
121     {
122         delete *it;
123     }
124 }
125
126 /**
127  * registers a callback that is entered as path below the main path.
128  * The configuration of the mainpath is done via DBusConfiguration.h
129  * @param vtable the vtable that holds a pointer to the callback that is called when the path is called from the dbus
130  * @param path the name of the path
131  * @param userdata pointer to the class that will handle the callback
132  */
133 void CAmDbusWrapper::registerCallback(const DBusObjectPathVTable* vtable, const std::string& path, void* userdata)
134 {
135     logInfo("DBusWrapper::registerCallback register callback:", path);
136
137     std::string completePath = std::string(DBUS_SERVICE_OBJECT_PATH) + "/" + path;
138     dbus_error_init(&mDBusError);
139     mpDbusConnection = dbus_bus_get(DBUS_BUS_SESSION, &mDBusError);
140     dbus_connection_register_object_path(mpDbusConnection, completePath.c_str(), vtable, userdata);
141     if (dbus_error_is_set(&mDBusError))
142     {
143         logError("DBusWrapper::registerCallack error: ", mDBusError.message);
144         dbus_error_free(&mDBusError);
145     }
146     mListNodes.push_back(path);
147 }
148
149 /**
150  * internal callback for the root introspection
151  * @param conn
152  * @param msg
153  * @param reference
154  * @return
155  */
156 DBusHandlerResult CAmDbusWrapper::cbRootIntrospection(DBusConnection *conn, DBusMessage *msg, void *reference)
157 {
158     logInfo("DBusWrapper::~cbRootIntrospection called:");
159
160     mpReference = (CAmDbusWrapper*) reference;
161     std::list<std::string> nodesList = mpReference->mListNodes;
162     DBusMessage * reply;
163     DBusMessageIter args;
164     dbus_uint32_t serial = 0;
165     if (dbus_message_is_method_call(msg, DBUS_INTERFACE_INTROSPECTABLE, "Introspect"))
166     {
167         std::list<std::string>::iterator nodeIter = nodesList.begin();
168         const char *xml = ROOT_INTROSPECT_XML;
169         std::stringstream introspect;
170         introspect << std::string(xml);
171         for (; nodeIter != nodesList.end(); ++nodeIter)
172         {
173             introspect << "<node name='" << nodeIter->c_str() << "'/>";
174         }
175         introspect << "</node>";
176
177         reply = dbus_message_new_method_return(msg);
178         std::string s = introspect.str();
179         const char* string = s.c_str();
180
181         // add the arguments to the reply
182         dbus_message_iter_init_append(reply, &args);
183         if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &string))
184         {
185             logError("DBusWrapper::~cbRootIntrospection DBUS Out Of Memory!");
186         }
187
188         // send the reply && flush the connection
189         if (!dbus_connection_send(conn, reply, &serial))
190         {
191             logError("DBusWrapper::~cbRootIntrospection DBUS Out Of Memory!");
192         }
193         dbus_connection_flush(conn);
194         // free the reply
195         dbus_message_unref(reply);
196
197         return (DBUS_HANDLER_RESULT_HANDLED);
198     }
199     else
200     {
201         return (DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
202     }
203 }
204
205 /**
206  * returns the dbus connection
207  * @param connection pointer to the connection
208  */
209 void CAmDbusWrapper::getDBusConnection(DBusConnection *& connection) const
210 {
211     connection = mpDbusConnection;
212 }
213
214 dbus_bool_t CAmDbusWrapper::addWatch(DBusWatch *watch, void *userData)
215 {
216     mpReference = (CAmDbusWrapper*) userData;
217     assert(mpReference!=0);
218     return (mpReference->addWatchDelegate(watch, userData));
219 }
220
221 dbus_bool_t CAmDbusWrapper::addWatchDelegate(DBusWatch * watch, void* userData)
222 {
223     (void) userData;
224     int16_t event = 0;
225     sh_pollHandle_t handle = 0;
226     uint flags = dbus_watch_get_flags(watch);
227
228     /* no watch flags for disabled watches */
229     if (dbus_watch_get_enabled(watch))
230     {
231         if (flags & DBUS_WATCH_READABLE)
232             event |= POLLIN;
233         if (flags & DBUS_WATCH_WRITABLE)
234             event |= POLLOUT;
235     }
236
237     logInfo("DBusWrapper::addWatchDelegate entered new watch, fd=", dbus_watch_get_unix_fd(watch), "event flag=", event);
238     am_Error_e error = mpSocketHandler->addFDPoll(dbus_watch_get_unix_fd(watch), event, NULL, &pDbusFireCallback, &pDbusCheckCallback, &pDbusDispatchCallback, watch, handle);
239
240     //if everything is alright, add the watch and the handle to our map so we know this relationship
241     if (error == E_OK && handle != 0)
242     {
243         mMapHandleWatch.insert(std::make_pair(watch, handle));
244         return (true);
245     }
246     logError("DBusWrapper::addWatchDelegate entering watch failed");
247     return (true);
248 }
249
250 void CAmDbusWrapper::removeWatch(DBusWatch *watch, void *userData)
251 {
252     mpReference = (CAmDbusWrapper*) userData;
253     assert(mpReference!=0);
254     mpReference->removeWatchDelegate(watch, userData);
255 }
256
257 void CAmDbusWrapper::removeWatchDelegate(DBusWatch *watch, void *userData)
258 {
259     (void) userData;
260     std::map<DBusWatch*, sh_pollHandle_t>::iterator iterator = mMapHandleWatch.begin();
261     iterator = mMapHandleWatch.find(watch);
262     if (iterator != mMapHandleWatch.end())
263         mpSocketHandler->removeFDPoll(iterator->second);
264     logInfo("DBusWrapper::removeWatch removed watch with handle", iterator->second);
265     mMapHandleWatch.erase(iterator);
266 }
267
268 void CAmDbusWrapper::toogleWatch(DBusWatch *watch, void *userData)
269 {
270     mpReference = (CAmDbusWrapper*) userData;
271     assert(mpReference!=0);
272     mpReference->toogleWatchDelegate(watch, userData);
273 }
274
275 void CAmDbusWrapper::toogleWatchDelegate(DBusWatch *watch, void *userData)
276 {
277     (void) userData;
278     int16_t event = 0;
279     dbus_watch_get_unix_fd(watch);
280     uint flags = dbus_watch_get_flags(watch);
281     /* no watch flags for disabled watches */
282     if (dbus_watch_get_enabled(watch))
283     {
284         if (flags & DBUS_WATCH_READABLE)
285             event |= POLLIN;
286         if (flags & DBUS_WATCH_WRITABLE)
287             event |= POLLOUT;
288     }
289     std::map<DBusWatch*, sh_pollHandle_t>::iterator iterator = mMapHandleWatch.begin();
290     iterator = mMapHandleWatch.find(watch);
291     if (iterator != mMapHandleWatch.end())
292         mpSocketHandler->updateEventFlags(iterator->second, event);
293     logInfo("DBusWrapper::toogleWatchDelegate watch was toggeled");
294 }
295
296 dbus_bool_t CAmDbusWrapper::addTimeout(DBusTimeout *timeout, void* userData)
297 {
298     mpReference = (CAmDbusWrapper*) userData;
299     assert(mpReference!=0);
300     return (mpReference->addTimeoutDelegate(timeout, userData));
301 }
302
303 dbus_bool_t CAmDbusWrapper::addTimeoutDelegate(DBusTimeout *timeout, void* userData)
304 {
305     if (!dbus_timeout_get_enabled(timeout))
306         return (false);
307
308     //calculate the timeout in timeval
309     timespec pollTimeout;
310     int localTimeout = dbus_timeout_get_interval(timeout);
311     pollTimeout.tv_sec = localTimeout / 1000;
312     pollTimeout.tv_nsec = (localTimeout % 1000) * 1000000;
313
314     //prepare handle and callback. new is eval, but there is no other choice because we need the pointer!
315     sh_timerHandle_t* handle = new sh_timerHandle_t;
316     mpListTimerhandles.push_back(handle);
317
318     //add the timer to the pollLoop
319     mpSocketHandler->addTimer(pollTimeout, &pDbusTimerCallback, *handle, timeout);
320
321     //save the handle with dbus context
322     dbus_timeout_set_data(timeout, handle, NULL);
323
324     //save timeout in Socket context
325     userData = timeout;
326     logInfo("DBusWrapper::addTimeoutDelegate a timeout was added");
327     return (true);
328 }
329
330 void CAmDbusWrapper::removeTimeout(DBusTimeout *timeout, void* userData)
331 {
332     mpReference = (CAmDbusWrapper*) userData;
333     assert(mpReference!=0);
334     mpReference->removeTimeoutDelegate(timeout, userData);
335 }
336
337 void CAmDbusWrapper::removeTimeoutDelegate(DBusTimeout *timeout, void* userData)
338 {
339     (void) userData;
340     //get the pointer to the handle and remove the timer
341     sh_timerHandle_t* handle = (sh_timerHandle_t*) dbus_timeout_get_data(timeout);
342     mpSocketHandler->removeTimer(*handle);
343
344     //now go throught the timerlist and remove the pointer, free memory
345     std::vector<sh_timerHandle_t*>::iterator it = mpListTimerhandles.begin();
346     for (; it != mpListTimerhandles.end(); ++it)
347     {
348         if (*it == handle)
349         {
350             mpListTimerhandles.erase(it);
351             break;
352         }
353     }
354     delete handle;
355     logInfo("DBusWrapper::removeTimeoutDelegate a timeout was removed");
356 }
357
358 void CAmDbusWrapper::toggleTimeout(DBusTimeout *timeout, void* userData)
359 {
360     mpReference = (CAmDbusWrapper*) userData;
361     assert(mpReference!=0);
362     mpReference->toggleTimeoutDelegate(timeout, userData);
363 }
364
365 bool am::CAmDbusWrapper::dbusDispatchCallback(const sh_pollHandle_t handle, void *userData)
366 {
367     (void) handle;
368     (void) userData;
369     bool returnVal = true;
370     dbus_connection_ref(mpDbusConnection);
371     if (dbus_connection_dispatch(mpDbusConnection) == DBUS_DISPATCH_COMPLETE)
372         returnVal = false;
373     dbus_connection_unref(mpDbusConnection);
374     //logInfo("DBusWrapper::dbusDispatchCallback was called");
375     return (returnVal);
376 }
377
378 bool am::CAmDbusWrapper::dbusCheckCallback(const sh_pollHandle_t handle, void *userData)
379 {
380     (void) handle;
381     (void) userData;
382     bool returnVal = false;
383     dbus_connection_ref(mpDbusConnection);
384     if (dbus_connection_get_dispatch_status(mpDbusConnection) == DBUS_DISPATCH_DATA_REMAINS)
385         returnVal = true;
386     dbus_connection_unref(mpDbusConnection);
387     //logInfo("DBusWrapper::dbusCheckCallback was called");
388     return (returnVal);
389 }
390
391 void am::CAmDbusWrapper::dbusFireCallback(const pollfd pollfd, const sh_pollHandle_t handle, void *userData)
392 {
393     (void) handle;
394     (void) userData;
395     assert(userData!=NULL);
396     uint flags = 0;
397
398     if (pollfd.revents & POLLIN)
399         flags |= DBUS_WATCH_READABLE;
400     if (pollfd.revents & POLLOUT)
401         flags |= DBUS_WATCH_WRITABLE;
402     if (pollfd.revents & POLLHUP)
403         flags |= DBUS_WATCH_HANGUP;
404     if (pollfd.revents & POLLERR)
405         flags |= DBUS_WATCH_ERROR;
406
407     DBusWatch *watch = (DBusWatch*) userData;
408
409     dbus_connection_ref(mpDbusConnection);
410     dbus_watch_handle(watch, flags);
411     dbus_connection_unref(mpDbusConnection);
412     //logInfo("DBusWrapper::dbusFireCallback was called");
413 }
414
415 void CAmDbusWrapper::toggleTimeoutDelegate(DBusTimeout *timeout, void* userData)
416 {
417     (void) userData;
418     //get the pointer to the handle and remove the timer
419     sh_timerHandle_t* handle = (sh_timerHandle_t*) dbus_timeout_get_data(timeout);
420
421     //stop or restart?
422     if (dbus_timeout_get_enabled(timeout))
423     {
424         //calculate the timeout in timeval
425         timespec pollTimeout;
426         int localTimeout = dbus_timeout_get_interval(timeout);
427         pollTimeout.tv_sec = localTimeout / 1000;
428         pollTimeout.tv_nsec = (localTimeout % 1000) * 1000000;
429         mpSocketHandler->updateTimer(*handle, pollTimeout);
430     }
431     else
432     {
433         mpSocketHandler->stopTimer(*handle);
434     }
435     logInfo("DBusWrapper::toggleTimeoutDelegate was called");
436 }
437
438 void CAmDbusWrapper::dbusTimerCallback(sh_timerHandle_t handle, void *userData)
439 {
440     assert(userData!=NULL);
441     if (dbus_timeout_get_enabled((DBusTimeout*) userData))
442     {
443         mpSocketHandler->restartTimer(handle);
444     }
445     dbus_timeout_handle((DBusTimeout*) userData);
446     logInfo("DBusWrapper::dbusTimerCallback was called");
447 }
448 }
449