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