* rework of buildsystem
[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
36 #include <iostream>  //todo remove
37
38 namespace am {
39
40 SocketHandler::SocketHandler()
41         :mListPoll(),
42          mListTimer(),
43          mListActiveTimer(),
44          mNextTimer(),
45          mLastInsertedHandle(0),
46          mDispatch(true),
47          mRecreatePollfds(true)
48 {
49         mTimeout.tv_nsec=-1;
50         mTimeout.tv_sec=-1;
51 }
52
53 SocketHandler::~SocketHandler()
54 {
55 }
56
57
58 //todo: maybe have some: give me more time returned?
59 /**
60  * start the block listening for filedescriptors. This is the mainloop.
61  */
62 void SocketHandler::start_listenting()
63 {
64         int16_t pollStatus;
65         std::list<int16_t>hitList;
66
67         //init the timer
68         initTimer();
69
70         while (mDispatch)
71         {
72                 //first we go through the registered filedescriptors and check if someone needs preparation:
73                 mListPoll_t::iterator prepIter=mListPoll.begin();
74                 shPollPrepare *prep=NULL;
75                 for(;prepIter!=mListPoll.end();++prepIter)
76                 {
77                         if((prep=prepIter->prepareCB)!=NULL) prep->Call(prepIter->handle,prepIter->userData);
78                 }
79
80                 if(mRecreatePollfds)
81                 {
82                         //there was a change in the setup, so we need to recreate the fdarray from the list
83                         std::for_each(mListPoll.begin(),mListPoll.end(),CopyPollfd(mfdPollingArray));
84                         mRecreatePollfds=false;
85                 }
86
87                 //block until something is on a filedescriptor
88                 if((pollStatus=poll(&mfdPollingArray.front(),mfdPollingArray.size(),timespec2ms(mTimeout)))==-1)
89                 {
90                         //todo enter DLT message here;
91                 }
92
93                 if (pollStatus!=0) //only check filedescriptors if there was a change
94                 {
95                         //todo: here could be a timer that makes sure naughty plugins return!
96
97                         //first, we check what happened:
98                         mPollfd_t::iterator iterator=mfdPollingArray.begin();
99                         mPollfd_t::iterator iteratorEnd=mfdPollingArray.end();
100                         for(;iterator!=iteratorEnd;++iterator)
101                         {
102                                 //get all indexes of the fired events and save them int hitList
103                                 hitList.clear();
104                                 std::vector<pollfd>::iterator it=mfdPollingArray.begin();
105                                 do
106                                 {
107                                         it=std::find_if(it,mfdPollingArray.end(),onlyFiredEvents);
108                                         if (it!=mfdPollingArray.end()) hitList.push_back(std::distance(mfdPollingArray.begin(), it++));
109
110                                 } while (it!=mfdPollingArray.end());
111
112                                 //stage 1, call firedCB for all matched events, but only if callback is not zero!
113                                 std::list<int16_t>::iterator hListIt=hitList.begin();
114                                 for(;hListIt!=hitList.end();++hListIt)
115                                 {
116                                         shPollFired* fire=NULL;
117                                         if ((fire=mListPoll.at(*hListIt).firedCB)!=NULL) fire->Call(mfdPollingArray.at(*hListIt),mListPoll.at(*hListIt).handle,mListPoll.at(*hListIt).userData);
118                                 }
119
120                                 //stage 2, lets ask around if some dispatching is necessary, if not, they are taken from the hitlist
121                                 hListIt=hitList.begin();
122                                 for(;hListIt!=hitList.end();++hListIt)
123                                 {
124                                         shPollCheck* check=NULL;
125                                         if ((check=mListPoll.at(*hListIt).checkCB)!=NULL)
126                                         {
127                                                 if (!check->Call(mListPoll.at(*hListIt).handle,mListPoll.at(*hListIt).userData))
128                                                 {
129                                                         hListIt=hitList.erase(hListIt);
130                                                 }
131                                         }
132                                 }
133
134                                 //stage 3, the ones left need to dispatch, we do this as long as there is something to dispatch..
135                                 do
136                                 {
137                                         hListIt=hitList.begin();
138                                         for(;hListIt!=hitList.end();++hListIt)
139                                         {
140                                                 shPollDispatch *dispatch=NULL;
141                                                 if((dispatch=mListPoll.at(*hListIt).dispatchCB)!=NULL)
142                                                 {
143                                                         if (dispatch->Call(mListPoll.at(*hListIt).handle,mListPoll.at(*hListIt).userData))
144                                                         {
145                                                                 hListIt=hitList.erase(hListIt);
146                                                         }
147                                                 }
148                                         }
149                                 } while (!hitList.empty());
150                         }
151                 }
152                 else //Timerevent
153                 {
154                         //this was a timer event, we need to take care about the timers
155                         timerUp();
156                 }
157         }
158 }
159
160 /**
161  * exits the loop
162  */
163 void SocketHandler::stop_listening()
164 {
165         mDispatch=false;
166 }
167
168 /**
169  * Adds a filedescriptor to the polling loop
170  * @param fd this is a valid filedescriptor
171  * @param event the event flags
172  * @param callback the callback that shall be called if the filedescriptor poll succeeded
173  * @return E_OK if the descriptor was added, E_NON_EXISTENT if the fd is not valid
174  */
175 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)
176 {
177         if (!fdIsValid(fd)) return E_NON_EXISTENT;
178
179         sh_poll_s pollData;
180         pollData.handle=++mLastInsertedPollHandle;
181         pollData.pollfdValue.fd=fd;
182         pollData.pollfdValue.events=event;
183         pollData.pollfdValue.revents=0;
184         pollData.userData=userData;
185         pollData.prepareCB=prepare;
186         pollData.firedCB=fired;
187         pollData.checkCB=check;
188         pollData.dispatchCB=dispatch;
189
190         //add new data to the list
191         mListPoll.push_back(pollData);
192
193         mRecreatePollfds=true;
194
195         handle=pollData.handle;
196         return E_OK;
197 }
198
199 /**
200  * removes a filedescriptor from the poll loop
201  * @param fd the filedescriptor to be removed
202  * @return E_OK in case of sucess, E_NON_EXISTENT or E_UNKNOWN if the fd in not registered
203  */
204 am_Error_e SocketHandler::removeFDPoll(const sh_pollHandle_t handle)
205 {
206         mListPoll_t::iterator iterator=mListPoll.begin();
207
208         for (;iterator!=mListPoll.end();++iterator)
209         {
210                 if(iterator->handle==handle)
211                 {
212                         iterator=mListPoll.erase(iterator);
213                         mRecreatePollfds=true;
214                         return E_OK;
215                 }
216         }
217         return E_UNKNOWN;
218 }
219
220 /**
221  * adds a timer to the list of timers. The callback will be fired when the timer is up.
222  * This is not a high precise timer, it is very coarse. It is meant to be used for timeouts when waiting
223  * for an answer via a filedescriptor.
224  * One time timer. If you need again a timer, you need to add a new timer in the callback of the old one.
225  * @param timeouts time until the callback is fired
226  * @param callback the callback
227  * @param handle the handle that is created for the timer is returned. Can be used to remove the timer
228  * @return E_OK in case of success
229  */
230 am_Error_e SocketHandler::addTimer(const timespec timeouts,TBasicTimerCallback*& callback,sh_timerHandle_t& handle, void * userData)
231 {
232         assert(!((timeouts.tv_sec==0) && (timeouts.tv_nsec==0)));
233         assert(callback!=NULL);
234
235         timer_s timerItem;
236
237         //create a new handle for the timer
238         handle=++mLastInsertedHandle;  //todo: overflow ruling !
239         timerItem.handle=handle;
240         timerItem.countdown=timeouts;
241         timerItem.timeout=timeouts;
242         timerItem.callback=callback;
243         timerItem.userData=userData;
244
245         //add timer to the list
246         mListActiveTimer.push_back(timerItem);
247         mListTimer.push_back(timerItem);
248
249         //very important: sort the list so that the smallest value is front
250         mListActiveTimer.sort(compareCountdown);
251         mTimeout=mListActiveTimer.front().countdown;
252         return E_OK;
253 }
254
255 /**
256  * removes a timer from the list of timers
257  * @param handle the handle to the timer
258  * @return E_OK in case of success, E_UNKNOWN if timer was not found.
259  */
260 am_Error_e SocketHandler::removeTimer(const sh_timerHandle_t handle)
261 {
262         assert(handle!=0);
263
264         //stop the current timer
265         stopTimer(handle);
266
267         std::list<timer_s>::iterator it=mListTimer.begin();
268         for(;it!=mListTimer.end();++it)
269         {
270                 if(it->handle==handle)
271                 {
272                         it=mListTimer.erase(it);
273                         return E_OK;
274                 }
275         }
276         return E_UNKNOWN;
277 }
278
279 /**
280  * restarts a timer and updates with a new interval
281  * @param handle handle to the timer
282  * @param timeouts new timout time
283  * @return E_OK on success, E_NON_EXISTENT if the handle was not found
284  */
285 am_Error_e SocketHandler::restartTimer(const sh_timerHandle_t handle, const timespec timeouts)
286 {
287         timer_s timerItem;
288         std::list<timer_s>::iterator it=mListTimer.begin();
289         for(;it!=mListTimer.end();++it)
290         {
291                 if (it->handle==handle)
292                 {
293                         timerItem=*it;
294                         break;
295                 }
296         }
297
298         if (timeouts.tv_nsec!=-1 && timeouts.tv_sec!=-1)
299         {
300                 timerItem.timeout=timeouts;
301         }
302
303         mListActiveTimer.push_back(timerItem);
304
305         //very important: sort the list so that the smallest value is front
306         mListActiveTimer.sort(compareCountdown);
307         mTimeout=mListActiveTimer.front().countdown;
308         return E_OK;
309 }
310
311 am_Error_e SocketHandler::stopTimer(const sh_timerHandle_t handle)
312 {
313         //go through the list and remove the timer with the handle
314         std::list<timer_s>::iterator it=mListActiveTimer.begin();
315         for(;it!=mListActiveTimer.end();++it)
316         {
317                 if(it->handle==handle)
318                 {
319                         it=mListActiveTimer.erase(it);
320                         if (!mListActiveTimer.empty())
321                         {
322                                 mTimeout=mListActiveTimer.front().countdown;
323                         }
324                         else
325                         {
326                                 mTimeout.tv_nsec=-1;
327                                 mTimeout.tv_sec=-1;
328                         }
329                         return E_OK;
330                 }
331         }
332         return E_NON_EXISTENT;
333 }
334
335 /**
336  * updates the eventFlags of a poll
337  * @param fd the filedescriptor of the poll
338  * @param event the event flags
339  * @return E_OK on succsess, E_NON_EXISTENT if fd was not found
340  */
341 am_Error_e SocketHandler::updateEventFlags(const sh_pollHandle_t handle, const int16_t  events)
342 {
343         mListPoll_t::iterator iterator=mListPoll.begin();
344
345         for (;iterator!=mListPoll.end();++iterator)
346         {
347                 if(iterator->handle==handle)
348                 {
349                         iterator->pollfdValue.events=events;
350                         mRecreatePollfds=true;
351                         return E_OK;
352                 }
353         }
354         return E_UNKNOWN;
355 }
356
357 /**
358  * checks if a filedescriptor is valid
359  * @param fd the filedescriptor
360  * @return true if the fd is valid
361  */
362 bool SocketHandler::fdIsValid(const int fd) const
363 {
364         return (fcntl(fd, F_GETFL) != -1 || errno != EBADF);
365 }
366
367 /**
368  * whenever a timer is up, this function needs to be called.
369  * Removes the fired timer, calls the callback and resets mTimeout
370  */
371 void SocketHandler::timerUp()
372 {
373         //first fire the event
374         mListActiveTimer.front().callback->Call(mListActiveTimer.front().handle,mListActiveTimer.front().userData);
375
376         //then remove the first timer, the one who fired
377         mListActiveTimer.pop_front();
378         if(!mListActiveTimer.empty())
379         {
380                 //substract the old value from all timers in the list
381                 std::for_each(mListActiveTimer.begin(),mListActiveTimer.end(),SubstractTime(mTimeout));
382                 mTimeout=mListActiveTimer.front().countdown;
383         }
384         else
385         {
386                 mTimeout.tv_nsec=-1;
387                 mTimeout.tv_sec=-1;
388         }
389 }
390
391 /**
392  * init the timers
393  */
394 void SocketHandler::initTimer()
395 {
396         if(!mListActiveTimer.empty())
397         {
398                 mTimeout=mListActiveTimer.front().countdown;
399         }
400         else
401         {
402                 mTimeout.tv_nsec=-1;
403                 mTimeout.tv_sec=-1;
404         }
405 }
406
407
408 /**
409 * convert timespec to milliseconds
410 * @param time time in timespec
411 * @return time in milliseconds
412 */
413 inline int SocketHandler::timespec2ms(const timespec & time)
414 {
415         return (time.tv_nsec == -1 && time.tv_sec == -1) ? -1 : time.tv_sec * 1000 + time.tv_nsec / 1000000;
416 }
417
418 /**
419 * functor to easy substract from each countdown
420 * @param t value to substract from
421 */
422 void SocketHandler::SubstractTime::operator ()(timer_s & t) const
423 {
424         int val = 0;
425         if((val = t.countdown.tv_nsec - param.tv_nsec) < 0){
426                 t.countdown.tv_nsec = 1000000000 + val;
427                 t.countdown.tv_sec--;
428         }else{
429                 t.countdown.tv_nsec = val;
430         }
431         (t.countdown.tv_sec - param.tv_sec) < 0 ? 0 : (t.countdown.tv_sec -= param.tv_sec);
432 }
433
434 void SocketHandler::CopyPollfd::operator ()(const sh_poll_s & row)
435 {
436         mArray.push_back(row.pollfdValue);
437 }
438
439 /* namespace am */
440 }
441