* assert on empty busname (routinginterface)
[profile/ivi/audiomanager.git] / AudioManagerDaemon / src / SocketHandler.cpp
1 /**
2 * Copyright (C) 2011, BMW AG
3 *
4 * GeniviAudioMananger AudioManagerDaemon
5 *
6 * \file DBusWrapper.cpp
7 *
8 * \date 20-Oct-2011 3:42:04 PM
9 * \author Christian Mueller (christian.ei.mueller@bmw.de)
10 *
11 * \section License
12 * GNU Lesser General Public License, version 2.1, with special exception (GENIVI clause)
13 * Copyright (C) 2011, BMW AG Christian Mueller  Christian.ei.mueller@bmw.de
14 *
15 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License, version 2.1, as published by the Free Software Foundation.
16 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License, version 2.1, for more details.
17 * You should have received a copy of the GNU Lesser General Public License, version 2.1, along with this program; if not, see <http://www.gnu.org/licenses/lgpl-2.1.html>.
18 * Note that the copyright holders assume that the GNU Lesser General Public License, version 2.1, may also be applicable to programs even in cases in which the program is not a library in the technical sense.
19 * Linking AudioManager statically or dynamically with other modules is making a combined work based on AudioManager. You may license such other modules under the GNU Lesser General Public License, version 2.1. If you do not want to license your linked modules under the GNU Lesser General Public License, version 2.1, you may use the program under the following exception.
20 * As a special exception, the copyright holders of AudioManager give you permission to combine AudioManager with software programs or libraries that are released under any license unless such a combination is not permitted by the license of such a software program or library. You may copy and distribute such a system following the terms of the GNU Lesser General Public License, version 2.1, including this special exception, for AudioManager and the licenses of the other code concerned.
21 * Note that people who make modified versions of AudioManager are not obligated to grant this special exception for their modified versions; it is their choice whether to do so. The GNU Lesser General Public License, version 2.1, gives permission to release a modified version without this exception; this exception also makes it possible to release a modified version which carries forward this exception.
22 *
23 */
24
25 #include "SocketHandler.h"
26 #include <assert.h>
27 #include <sys/fcntl.h>
28 #include <sys/errno.h>
29 #include <sys/poll.h>
30 #include <algorithm>
31 #include <time.h>
32 #include <features.h>
33
34 //todo: implement ppoll
35 //todo: signal handling here
36 //todo: implement time correction if timer was interrupted by call
37
38 #include <iostream>  //todo remove
39
40 namespace am {
41
42 SocketHandler::SocketHandler()
43         :mListPoll(),
44          mListTimer(),
45          mListActiveTimer(),
46          mNextTimer(),
47          mLastInsertedHandle(0),
48          mLastInsertedPollHandle(0),
49          mDispatch(true),
50          mRecreatePollfds(true)
51 {
52         mTimeout.tv_nsec=-1;
53         mTimeout.tv_sec=-1;
54 }
55
56 SocketHandler::~SocketHandler()
57 {
58 }
59
60
61 //todo: maybe have some: give me more time returned?
62 /**
63  * start the block listening for filedescriptors. This is the mainloop.
64  */
65 void SocketHandler::start_listenting()
66 {
67         int16_t pollStatus;
68         std::list<int16_t>hitList;
69
70         //init the timer
71         initTimer();
72
73         while (mDispatch)
74         {
75                 //first we go through the registered filedescriptors and check if someone needs preparation:
76                 mListPoll_t::iterator prepIter=mListPoll.begin();
77                 shPollPrepare *prep=NULL;
78                 for(;prepIter!=mListPoll.end();++prepIter)
79                 {
80                         if((prep=prepIter->prepareCB)!=NULL) prep->Call(prepIter->handle,prepIter->userData);
81                 }
82
83                 if(mRecreatePollfds)
84                 {
85                         mfdPollingArray.clear();
86                         //there was a change in the setup, so we need to recreate the fdarray from the list
87                         std::for_each(mListPoll.begin(),mListPoll.end(),CopyPollfd(mfdPollingArray));
88                         mRecreatePollfds=false;
89                 }
90
91                 //block until something is on a filedescriptor
92                 if((pollStatus=poll(&mfdPollingArray.front(),mfdPollingArray.size(),timespec2ms(mTimeout)))==-1)
93                 {
94                         //todo enter DLT message here;
95                 }
96
97                 if (pollStatus!=0) //only check filedescriptors if there was a change
98                 {
99                         //todo: here could be a timer that makes sure naughty plugins return!
100
101                         //get all indexes of the fired events and save them int hitList
102                         hitList.clear();
103                         std::vector<pollfd>::iterator it=mfdPollingArray.begin();
104                         do
105                         {
106                                 it=std::find_if(it,mfdPollingArray.end(),onlyFiredEvents);
107                                 if (it!=mfdPollingArray.end()) hitList.push_back(std::distance(mfdPollingArray.begin(), it++));
108
109                         } while (it!=mfdPollingArray.end());
110
111                         //stage 1, call firedCB for all matched events, but only if callback is not zero!
112                         std::list<int16_t>::iterator hListIt=hitList.begin();
113                         for(;hListIt!=hitList.end();++hListIt)
114                         {
115                                 shPollFired* fire=NULL;
116                                 if ((fire=mListPoll.at(*hListIt).firedCB)!=NULL) fire->Call(mfdPollingArray.at(*hListIt),mListPoll.at(*hListIt).handle,mListPoll.at(*hListIt).userData);
117                         }
118
119                         //stage 2, lets ask around if some dispatching is necessary, if not, they are taken from the hitlist
120                         hListIt=hitList.begin();
121                         for(;hListIt!=hitList.end();++hListIt)
122                         {
123                                 shPollCheck* check=NULL;
124                                 if ((check=mListPoll.at(*hListIt).checkCB)!=NULL)
125                                 {
126                                         if (!check->Call(mListPoll.at(*hListIt).handle,mListPoll.at(*hListIt).userData))
127                                         {
128                                                 hListIt=hitList.erase(hListIt);
129                                         }
130                                 }
131                         }
132
133                         //stage 3, the ones left need to dispatch, we do this as long as there is something to dispatch..
134                         do
135                         {
136                                 hListIt=hitList.begin();
137                                 for(;hListIt!=hitList.end();++hListIt)
138                                 {
139                                         shPollDispatch *dispatch=NULL;
140                                         if((dispatch=mListPoll.at(*hListIt).dispatchCB)!=NULL)
141                                         {
142                                                 if (!dispatch->Call(mListPoll.at(*hListIt).handle,mListPoll.at(*hListIt).userData))
143                                                 {
144                                                         hListIt=hitList.erase(hListIt);
145                                                 }
146                                         }
147                                         else //there is no dispatch function, so we just remove the file from the list...
148                                         {
149                                                 hListIt=hitList.erase(hListIt);
150                                         }
151                                 }
152                         } while (!hitList.empty());
153
154                 }
155                 else //Timerevent
156                 {
157                         //this was a timer event, we need to take care about the timers
158                         timerUp();
159                 }
160         }
161 }
162
163 /**
164  * exits the loop
165  */
166 void SocketHandler::stop_listening()
167 {
168         mDispatch=false;
169 }
170
171 /**
172  * Adds a filedescriptor to the polling loop
173  * @param fd this is a valid filedescriptor
174  * @param event the event flags
175  * @param callback the callback that shall be called if the filedescriptor poll succeeded
176  * @return E_OK if the descriptor was added, E_NON_EXISTENT if the fd is not valid
177  */
178 am_Error_e SocketHandler::addFDPoll(const int fd,const int16_t event, shPollPrepare *prepare,shPollFired *fired,shPollCheck *check,shPollDispatch *dispatch, void* userData,sh_pollHandle_t& handle)
179 {
180         if (!fdIsValid(fd)) return E_NON_EXISTENT;
181
182         sh_poll_s pollData;
183         pollData.pollfdValue.fd=fd;
184         pollData.handle=++mLastInsertedPollHandle;
185         pollData.pollfdValue.events=event;
186         pollData.pollfdValue.revents=0;
187         pollData.userData=userData;
188         pollData.prepareCB=prepare;
189         pollData.firedCB=fired;
190         pollData.checkCB=check;
191         pollData.dispatchCB=dispatch;
192
193         //add new data to the list
194         mListPoll.push_back(pollData);
195
196         mRecreatePollfds=true;
197
198         handle=pollData.handle;
199         return E_OK;
200 }
201
202 /**
203  * removes a filedescriptor from the poll loop
204  * @param fd the filedescriptor to be removed
205  * @return E_OK in case of sucess, E_NON_EXISTENT or E_UNKNOWN if the fd in not registered
206  */
207 am_Error_e SocketHandler::removeFDPoll(const sh_pollHandle_t handle)
208 {
209         mListPoll_t::iterator iterator=mListPoll.begin();
210
211         for (;iterator!=mListPoll.end();++iterator)
212         {
213                 if(iterator->handle==handle)
214                 {
215                         iterator=mListPoll.erase(iterator);
216                         mRecreatePollfds=true;
217                         return E_OK;
218                 }
219         }
220         return E_UNKNOWN;
221 }
222
223 /**
224  * adds a timer to the list of timers. The callback will be fired when the timer is up.
225  * This is not a high precise timer, it is very coarse. It is meant to be used for timeouts when waiting
226  * for an answer via a filedescriptor.
227  * One time timer. If you need again a timer, you need to add a new timer in the callback of the old one.
228  * @param timeouts time until the callback is fired
229  * @param callback the callback
230  * @param handle the handle that is created for the timer is returned. Can be used to remove the timer
231  * @return E_OK in case of success
232  */
233 am_Error_e SocketHandler::addTimer(const timespec timeouts,shTimerCallBack*& callback,sh_timerHandle_t& handle, void * userData)
234 {
235         assert(!((timeouts.tv_sec==0) && (timeouts.tv_nsec==0)));
236         assert(callback!=NULL);
237
238         timer_s timerItem;
239
240         //create a new handle for the timer
241         handle=++mLastInsertedHandle;  //todo: overflow ruling !
242         timerItem.handle=handle;
243         timerItem.countdown=timeouts;
244         timerItem.timeout=timeouts;
245         timerItem.callback=callback;
246         timerItem.userData=userData;
247
248         //add timer to the list
249         mListActiveTimer.push_back(timerItem);
250         mListTimer.push_back(timerItem);
251
252         //very important: sort the list so that the smallest value is front
253         mListActiveTimer.sort(compareCountdown);
254         mTimeout=mListActiveTimer.front().countdown;
255         return E_OK;
256 }
257
258 /**
259  * removes a timer from the list of timers
260  * @param handle the handle to the timer
261  * @return E_OK in case of success, E_UNKNOWN if timer was not found.
262  */
263 am_Error_e SocketHandler::removeTimer(const sh_timerHandle_t handle)
264 {
265         assert(handle!=0);
266
267         //stop the current timer
268         stopTimer(handle);
269
270         std::list<timer_s>::iterator it=mListTimer.begin();
271         for(;it!=mListTimer.end();++it)
272         {
273                 if(it->handle==handle)
274                 {
275                         it=mListTimer.erase(it);
276                         return E_OK;
277                 }
278         }
279         return E_UNKNOWN;
280 }
281
282 /**
283  * restarts a timer and updates with a new interval
284  * @param handle handle to the timer
285  * @param timeouts new timout time
286  * @return E_OK on success, E_NON_EXISTENT if the handle was not found
287  */
288 am_Error_e SocketHandler::restartTimer(const sh_timerHandle_t handle, const timespec timeouts)
289 {
290         timer_s timerItem;
291         std::list<timer_s>::iterator it=mListTimer.begin();
292         for(;it!=mListTimer.end();++it)
293         {
294                 if (it->handle==handle)
295                 {
296                         timerItem=*it;
297                         break;
298                 }
299         }
300
301         if (timeouts.tv_nsec!=-1 && timeouts.tv_sec!=-1)
302         {
303                 timerItem.timeout=timeouts;
304         }
305
306         mListActiveTimer.push_back(timerItem);
307
308         //very important: sort the list so that the smallest value is front
309         mListActiveTimer.sort(compareCountdown);
310         mTimeout=mListActiveTimer.front().countdown;
311         return E_OK;
312 }
313
314 am_Error_e SocketHandler::stopTimer(const sh_timerHandle_t handle)
315 {
316         //go through the list and remove the timer with the handle
317         std::list<timer_s>::iterator it=mListActiveTimer.begin();
318         for(;it!=mListActiveTimer.end();++it)
319         {
320                 if(it->handle==handle)
321                 {
322                         it=mListActiveTimer.erase(it);
323                         if (!mListActiveTimer.empty())
324                         {
325                                 mTimeout=mListActiveTimer.front().countdown;
326                         }
327                         else
328                         {
329                                 mTimeout.tv_nsec=-1;
330                                 mTimeout.tv_sec=-1;
331                         }
332                         return E_OK;
333                 }
334         }
335         return E_NON_EXISTENT;
336 }
337
338 /**
339  * updates the eventFlags of a poll
340  * @param fd the filedescriptor of the poll
341  * @param event the event flags
342  * @return E_OK on succsess, E_NON_EXISTENT if fd was not found
343  */
344 am_Error_e SocketHandler::updateEventFlags(const sh_pollHandle_t handle, const int16_t  events)
345 {
346         mListPoll_t::iterator iterator=mListPoll.begin();
347
348         for (;iterator!=mListPoll.end();++iterator)
349         {
350                 if(iterator->handle==handle)
351                 {
352                         iterator->pollfdValue.events=events;
353                         mRecreatePollfds=true;
354                         return E_OK;
355                 }
356         }
357         return E_UNKNOWN;
358 }
359
360 /**
361  * checks if a filedescriptor is valid
362  * @param fd the filedescriptor
363  * @return true if the fd is valid
364  */
365 bool SocketHandler::fdIsValid(const int fd) const
366 {
367         return (fcntl(fd, F_GETFL) != -1 || errno != EBADF);
368 }
369
370 /**
371  * whenever a timer is up, this function needs to be called.
372  * Removes the fired timer, calls the callback and resets mTimeout
373  */
374 void SocketHandler::timerUp()
375 {
376         //first fire the event
377         mListActiveTimer.front().callback->Call(mListActiveTimer.front().handle,mListActiveTimer.front().userData);
378
379         //then remove the first timer, the one who fired
380         mListActiveTimer.pop_front();
381         if(!mListActiveTimer.empty())
382         {
383                 //substract the old value from all timers in the list
384                 std::for_each(mListActiveTimer.begin(),mListActiveTimer.end(),SubstractTime(mTimeout));
385                 mTimeout=mListActiveTimer.front().countdown;
386         }
387         else
388         {
389                 mTimeout.tv_nsec=-1;
390                 mTimeout.tv_sec=-1;
391         }
392 }
393
394 /**
395  * init the timers
396  */
397 void SocketHandler::initTimer()
398 {
399         if(!mListActiveTimer.empty())
400         {
401                 mTimeout=mListActiveTimer.front().countdown;
402         }
403         else
404         {
405                 mTimeout.tv_nsec=-1;
406                 mTimeout.tv_sec=-1;
407         }
408 }
409
410
411 /**
412 * convert timespec to milliseconds
413 * @param time time in timespec
414 * @return time in milliseconds
415 */
416 inline int SocketHandler::timespec2ms(const timespec & time)
417 {
418         return (time.tv_nsec == -1 && time.tv_sec == -1) ? -1 : time.tv_sec * 1000 + time.tv_nsec / 1000000;
419 }
420
421 /**
422 * functor to easy substract from each countdown
423 * @param t value to substract from
424 */
425 void SocketHandler::SubstractTime::operator ()(timer_s & t) const
426 {
427         int val = 0;
428         if((val = t.countdown.tv_nsec - param.tv_nsec) < 0){
429                 t.countdown.tv_nsec = 1000000000 + val;
430                 t.countdown.tv_sec--;
431         }else{
432                 t.countdown.tv_nsec = val;
433         }
434         (t.countdown.tv_sec - param.tv_sec) < 0 ? 0 : (t.countdown.tv_sec -= param.tv_sec);
435 }
436
437 void SocketHandler::CopyPollfd::operator ()(const sh_poll_s & row)
438 {
439         pollfd temp=row.pollfdValue;
440         temp.revents=0;
441         mArray.push_back(temp);
442 }
443
444 /* namespace am */
445 }
446