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