0278d59a7fd8760ce086abbdafc92481e389321d
[framework/web/wrt-plugins-common.git] / src / modules / tizen / DBus / Connection.cpp
1 /*
2  * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *    Licensed under the Apache License, Version 2.0 (the "License");
5  *    you may not use this file except in compliance with the License.
6  *    You may obtain a copy of the License at
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *    Unless required by applicable law or agreed to in writing, software
11  *    distributed under the License is distributed on an "AS IS" BASIS,
12  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *    See the License for the specific language governing permissions and
14  *    limitations under the License.
15  */
16 /**
17  * @author          Zbigniew Kostrzewa (z.kostrzewa@samsung.com)
18  */
19
20 #include "Connection.h"
21 #include <cassert>
22 #include <algorithm>
23 #include <Commons/Exception.h>
24 #include <dpl/log/log.h>
25 #include "Message.h"
26
27 namespace WrtDeviceApis {
28 namespace DBus {
29 Connection::Connection() :
30     m_connection(NULL),
31     m_messageDispatcher(NULL),
32     m_connected(false)
33 {
34     dbus_error_init(&m_error);
35     m_messageDispatcher = new MessageDispatcher(this);
36 }
37
38 Connection::~Connection()
39 {
40     assert(!m_connected && "DBus connection has not been closed.");
41     delete m_messageDispatcher;
42     if (dbus_error_is_set(&m_error)) {
43         dbus_error_free(&m_error);
44     }
45 }
46
47 void Connection::open(DBusBusType bus)
48 {
49     if (m_connected) { return; }
50
51     m_connection = dbus_bus_get(bus, &m_error);
52     if (!m_connection || dbus_error_is_set(&m_error)) {
53         std::string message = m_error.message;
54         dbus_error_free(&m_error);
55         ThrowMsg(Commons::PlatformException,
56                  "Couldn't attach to DBus system bus: " << message);
57     }
58
59     dbus_connection_set_exit_on_disconnect(m_connection, FALSE);
60
61     for (FilterSetIterator it = m_filters.begin(); it != m_filters.end();
62          ++it) {
63         addFilterInternal(*it);
64     }
65
66     if (!dbus_connection_set_watch_functions(m_connection, addWatch,
67                                              removeWatch, toggleWatch, this,
68                                              NULL)) {
69         ThrowMsg(Commons::PlatformException, "Couldn't set-up watch functions.");
70     }
71
72     if (!dbus_connection_add_filter(m_connection, filterMessage, this, NULL)) {
73         ThrowMsg(Commons::PlatformException,
74                  "Couldn't set signal handler callback.");
75     }
76
77     m_connected = true;
78 }
79
80 void Connection::close()
81 {
82     LogDebug("ENTER");
83     if (!m_connected) { return; }
84
85     dbus_connection_remove_filter(m_connection, filterMessage, this);
86
87     // Calls removeWatch() automagically.
88     if (!dbus_connection_set_watch_functions(m_connection, NULL, NULL, NULL,
89                                              NULL, NULL)) {
90         DPL::Mutex::ScopedLock lock(&m_watchesMutex);
91         WatchSetIterator it = m_watches.begin();
92         for (; it != m_watches.end(); ++it) {
93             if (it->second->isEnabled()) {
94                 m_messageDispatcher->removeDescriptor(it->second->getHandle(),
95                                                       it->second->getType());
96             }
97         }
98         m_watches.clear();
99     }
100
101     // TODO: think how to handle exception on filter removal; 1 - do nothing (now),
102     // 2 - remove the buggy one but throw an exception, 3 - remove the buggy one
103     // and don't throw any exception
104     for (FilterSetIterator it = m_filters.begin(); it != m_filters.end();
105          ++it) {
106         removeFilterInternal(*it);
107     }
108
109     dbus_connection_unref(m_connection);
110
111     m_connected = false;
112 }
113
114 void Connection::addFilter(const std::string& rule)
115 {
116     if (m_connected) {
117         addFilterInternal(rule);
118     }
119     m_filters.insert(rule);
120 }
121
122 void Connection::removeFilter(const std::string& rule)
123 {
124     if (m_connected) {
125         removeFilterInternal(rule);
126     }
127     m_filters.erase(rule);
128 }
129
130 void Connection::setWorkerThread(DPL::Thread* thread)
131 {
132     assert(!m_connected && "Connection has already been established.");
133     m_messageDispatcher->SwitchToThread(thread);
134 }
135
136 void Connection::addFilterInternal(const std::string& rule)
137 {
138     assert(m_connection && "Connection has to be established first.");
139     dbus_bus_add_match(m_connection, rule.c_str(), &m_error);
140     if (dbus_error_is_set(&m_error)) {
141         std::string message = m_error.message;
142         dbus_error_free(&m_error);
143         ThrowMsg(Commons::InvalidArgumentException, "Invalid rule: " << message);
144     }
145 }
146
147 void Connection::removeFilterInternal(const std::string& rule)
148 {
149     assert(m_connection && "Connection has to be established first.");
150     dbus_bus_remove_match(m_connection, rule.c_str(), &m_error);
151     if (dbus_error_is_set(&m_error)) {
152         std::string message = m_error.message;
153         dbus_error_free(&m_error);
154         ThrowMsg(Commons::InvalidArgumentException, "Invalid rule: " << message);
155     }
156 }
157
158 dbus_bool_t Connection::addWatch(DBusWatch* watch, void* data)
159 {
160     LogDebug("ENTER");
161     assert(data && "Connection should be passed as user data.");
162     Connection* this_ = static_cast<Connection*>(data);
163     WatchPtr wrapper(new Watch(watch));
164     if (wrapper->isEnabled()) {
165         this_->m_messageDispatcher->addDescriptor(wrapper->getHandle(),
166                                                   wrapper->getType());
167     }
168     DPL::Mutex::ScopedLock lock(&this_->m_watchesMutex);
169     this_->m_watches[wrapper->getHandle()] = wrapper;
170     return TRUE;
171 }
172
173 void Connection::removeWatch(DBusWatch* watch, void* data)
174 {
175     LogDebug("ENTER");
176     assert(data && "Connection should be passed as user data.");
177     Connection* this_ = static_cast<Connection*>(data);
178     Watch wrapper(watch);
179     if (wrapper.isEnabled()) {
180         this_->m_messageDispatcher->removeDescriptor(wrapper.getHandle(),
181                                                      wrapper.getType());
182     }
183     DPL::Mutex::ScopedLock lock(&this_->m_watchesMutex);
184     this_->m_watches.erase(wrapper.getHandle());
185 }
186
187 void Connection::toggleWatch(DBusWatch* watch, void* data)
188 {
189     LogDebug("ENTER");
190     assert(data && "Connection should be passed as user data.");
191     Connection* this_ = static_cast<Connection*>(data);
192     Watch wrapper(watch);
193     if (wrapper.isEnabled()) {
194         this_->m_messageDispatcher->addDescriptor(wrapper.getHandle(),
195                                                   wrapper.getType());
196     } else {
197         this_->m_messageDispatcher->removeDescriptor(wrapper.getHandle(),
198                                                      wrapper.getType());
199     }
200 }
201
202 DBusHandlerResult Connection::filterMessage(DBusConnection* /*connection*/,
203                                             DBusMessage* message,
204                                             void* data)
205 {
206     LogDebug("ENTER");
207     if (data) {
208         Connection* this_ = static_cast<Connection*>(data);
209         MessagePtr msg;
210         if (message) {
211             LogInfo(
212                 "DBUS message received from interface: " <<
213                 dbus_message_get_interface(message));
214             msg.Reset(new Message(message));
215         } else {
216             LogError("DBus message not set, this should not happen!");
217             return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
218         }
219
220         LogDebug("____HERE");
221         FilterSetIterator it = this_->m_filters.begin();
222         for (; it != this_->m_filters.end(); ++it) {
223             // TODO: extend following matching procedure to check not only
224             // interface's name.
225             if ((*it).find(msg->getInterface()) != std::string::npos) {
226                 LogDebug("______emitting...");
227                 MessageEvent event(msg);
228                 this_->EmitEvent(event);
229                 LogDebug("______done");
230             }
231         }
232     } else {
233         LogError("User-data not set, this should not happen!");
234     }
235     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
236 }
237
238 Connection::MessageDispatcher::MessageDispatcher(Connection* connection)
239     : m_connection(connection)
240 {
241     assert(m_connection && "Connection cannot be NULL.");
242     DPL::Event::ControllerEventHandler<AddDescriptorEvent>::Touch();
243     DPL::Event::ControllerEventHandler<RemoveDescriptorEvent>::Touch();
244 }
245
246 Connection::MessageDispatcher::~MessageDispatcher()
247 {
248     SwitchToThread(NULL);
249 }
250
251 void Connection::MessageDispatcher::addDescriptor(DPL::WaitableHandle handle,
252                                                   DPL::WaitMode::Type mode)
253 {
254     LogDebug("ENTER");
255     AddDescriptorEvent event(handle, mode);
256     DPL::Event::ControllerEventHandler<AddDescriptorEvent>::PostSyncEvent(event);
257 }
258
259 void Connection::MessageDispatcher::removeDescriptor(DPL::WaitableHandle handle,
260                                                      DPL::WaitMode::Type mode)
261 {
262     LogDebug("ENTER");
263     RemoveDescriptorEvent event(handle, mode);
264     DPL::Event::ControllerEventHandler<RemoveDescriptorEvent>::PostSyncEvent(event);
265 }
266
267 void Connection::MessageDispatcher::OnEventReceived(
268         const AddDescriptorEvent& event)
269 {
270     LogDebug("ENTER");
271     DPL::WaitableHandleWatchSupport::InheritedContext()->AddWaitableHandleWatch(
272         this,
273         event.GetArg0(),
274         event.GetArg1());
275 }
276
277 void Connection::MessageDispatcher::OnEventReceived(
278         const RemoveDescriptorEvent& event)
279 {
280     LogDebug("ENTER");
281     DPL::WaitableHandleWatchSupport::InheritedContext()->
282         RemoveWaitableHandleWatch(this, event.GetArg0(), event.GetArg1());
283 }
284
285 void Connection::MessageDispatcher::OnWaitableHandleEvent(
286         DPL::WaitableHandle waitableHandle,
287         DPL::WaitMode::Type mode)
288 {
289     LogDebug("ENTER");
290     {
291         DPL::Mutex::ScopedLock lock(&m_connection->m_watchesMutex);
292         WatchSetIterator it = m_connection->m_watches.find(waitableHandle);
293         if (it != m_connection->m_watches.end()) {
294             unsigned int flags =
295                 (mode == DPL::WaitMode::Write ? DBUS_WATCH_WRITABLE
296                         : DBUS_WATCH_READABLE);
297             while (!dbus_watch_handle(it->second->get(), flags)) {
298                 LogError("Too few memory!");
299                 // TODO: add some check-point to not loop infinitely
300             }
301         }
302         // TODO: not sure if there is point in dispatching connection when watch not found?
303     }
304     dbus_connection_ref(m_connection->m_connection);
305     while (dbus_connection_dispatch(m_connection->m_connection) ==
306            DBUS_DISPATCH_DATA_REMAINS) {
307     }
308     dbus_connection_unref(m_connection->m_connection);
309 }
310
311 Connection::Watch::Watch(DBusWatch* watch) : m_watch(watch)
312 {
313     assert(m_watch && "Watch cannot be NULL.");
314 }
315
316 DPL::WaitableHandle Connection::Watch::getHandle() const
317 {
318     return dbus_watch_get_unix_fd(m_watch);
319 }
320
321 DPL::WaitMode::Type Connection::Watch::getType() const
322 {
323     if ((dbus_watch_get_flags(m_watch) & DBUS_WATCH_WRITABLE) != 0) {
324         return DPL::WaitMode::Write;
325     }
326     return DPL::WaitMode::Read;
327 }
328
329 bool Connection::Watch::isEnabled() const
330 {
331     return (TRUE == dbus_watch_get_enabled(m_watch));
332 }
333
334 DBusWatch* Connection::Watch::get() const
335 {
336     return m_watch;
337 }
338 } // DBus
339 } // WrtDeviceApis