* add setSinkVolume to telnetserver
[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) :
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(DBUS_BUS_SESSION, &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     IAmShTimerCallBack* buffer = &pDbusTimerCallback;
318
319     //add the timer to the pollLoop
320     mpSocketHandler->addTimer(pollTimeout, buffer, *handle, timeout);
321
322     //save the handle with dbus context
323     dbus_timeout_set_data(timeout, handle, NULL);
324
325     //save timeout in Socket context
326     userData = timeout;
327     logInfo("DBusWrapper::addTimeoutDelegate a timeout was added");
328     return (true);
329 }
330
331 void CAmDbusWrapper::removeTimeout(DBusTimeout *timeout, void* userData)
332 {
333     mpReference = (CAmDbusWrapper*) userData;
334     assert(mpReference!=0);
335     mpReference->removeTimeoutDelegate(timeout, userData);
336 }
337
338 void CAmDbusWrapper::removeTimeoutDelegate(DBusTimeout *timeout, void* userData)
339 {
340     (void) userData;
341     //get the pointer to the handle and remove the timer
342     sh_timerHandle_t* handle = (sh_timerHandle_t*) dbus_timeout_get_data(timeout);
343     mpSocketHandler->removeTimer(*handle);
344
345     //now go throught the timerlist and remove the pointer, free memory
346     std::vector<sh_timerHandle_t*>::iterator it = mpListTimerhandles.begin();
347     for (; it != mpListTimerhandles.end(); ++it)
348     {
349         if (*it == handle)
350         {
351             mpListTimerhandles.erase(it);
352             break;
353         }
354     }
355     delete handle;
356     logInfo("DBusWrapper::removeTimeoutDelegate a timeout was removed");
357 }
358
359 void CAmDbusWrapper::toggleTimeout(DBusTimeout *timeout, void* userData)
360 {
361     mpReference = (CAmDbusWrapper*) userData;
362     assert(mpReference!=0);
363     mpReference->toggleTimeoutDelegate(timeout, userData);
364 }
365
366 bool am::CAmDbusWrapper::dbusDispatchCallback(const sh_pollHandle_t handle, void *userData)
367 {
368     (void) handle;
369     (void) userData;
370     bool returnVal = true;
371     dbus_connection_ref(mpDbusConnection);
372     if (dbus_connection_dispatch(mpDbusConnection) == DBUS_DISPATCH_COMPLETE)
373         returnVal = false;
374     dbus_connection_unref(mpDbusConnection);
375     //logInfo("DBusWrapper::dbusDispatchCallback was called");
376     return (returnVal);
377 }
378
379 bool am::CAmDbusWrapper::dbusCheckCallback(const sh_pollHandle_t handle, void *userData)
380 {
381     (void) handle;
382     (void) userData;
383     bool returnVal = false;
384     dbus_connection_ref(mpDbusConnection);
385     if (dbus_connection_get_dispatch_status(mpDbusConnection) == DBUS_DISPATCH_DATA_REMAINS)
386         returnVal = true;
387     dbus_connection_unref(mpDbusConnection);
388     //logInfo("DBusWrapper::dbusCheckCallback was called");
389     return (returnVal);
390 }
391
392 void am::CAmDbusWrapper::dbusFireCallback(const pollfd pollfd, const sh_pollHandle_t handle, void *userData)
393 {
394     (void) handle;
395     (void) userData;
396     assert(userData!=NULL);
397     uint flags = 0;
398
399     if (pollfd.revents & POLLIN)
400         flags |= DBUS_WATCH_READABLE;
401     if (pollfd.revents & POLLOUT)
402         flags |= DBUS_WATCH_WRITABLE;
403     if (pollfd.revents & POLLHUP)
404         flags |= DBUS_WATCH_HANGUP;
405     if (pollfd.revents & POLLERR)
406         flags |= DBUS_WATCH_ERROR;
407
408     DBusWatch *watch = (DBusWatch*) userData;
409
410     dbus_connection_ref(mpDbusConnection);
411     dbus_watch_handle(watch, flags);
412     dbus_connection_unref(mpDbusConnection);
413     //logInfo("DBusWrapper::dbusFireCallback was called");
414 }
415
416 void CAmDbusWrapper::toggleTimeoutDelegate(DBusTimeout *timeout, void* userData)
417 {
418     (void) userData;
419     //get the pointer to the handle and remove the timer
420     sh_timerHandle_t* handle = (sh_timerHandle_t*) dbus_timeout_get_data(timeout);
421
422     //stop or restart?
423     if (dbus_timeout_get_enabled(timeout))
424     {
425         //calculate the timeout in timeval
426         timespec pollTimeout;
427         int localTimeout = dbus_timeout_get_interval(timeout);
428         pollTimeout.tv_sec = localTimeout / 1000;
429         pollTimeout.tv_nsec = (localTimeout % 1000) * 1000000;
430         mpSocketHandler->restartTimer(*handle, pollTimeout);
431     }
432     else
433     {
434         mpSocketHandler->stopTimer(*handle);
435     }
436     logInfo("DBusWrapper::toggleTimeoutDelegate was called");
437 }
438
439 void CAmDbusWrapper::dbusTimerCallback(sh_timerHandle_t handle, void *userData)
440 {
441     assert(userData!=NULL);
442     if (dbus_timeout_get_enabled((DBusTimeout*) userData))
443     {
444         timespec ts;
445         ts.tv_nsec = -1;
446         ts.tv_sec = -1;
447         mpSocketHandler->restartTimer(handle, ts);
448     }
449     dbus_timeout_handle((DBusTimeout*) userData);
450     logInfo("DBusWrapper::dbusTimerCallback was called");
451 }
452 }
453