2 // Tizen Web Device API
3 // Copyright (c) 2012 Samsung Electronics Co., Ltd.
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
9 // http://www.apache.org/licenses/LICENSE-2.0
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.
18 #include "Connection.h"
21 #include <Commons/Exception.h>
22 #include <dpl/log/log.h>
28 Connection::Connection() :
30 m_messageDispatcher(NULL),
33 dbus_error_init(&m_error);
34 m_messageDispatcher = new MessageDispatcher(this);
37 Connection::~Connection()
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);
46 void Connection::open(DBusBusType bus)
48 if (m_connected) { return; }
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);
58 dbus_connection_set_exit_on_disconnect(m_connection, FALSE);
60 for (FilterSetIterator it = m_filters.begin(); it != m_filters.end();
62 addFilterInternal(*it);
65 if (!dbus_connection_set_watch_functions(m_connection, addWatch,
66 removeWatch, toggleWatch, this,
68 ThrowMsg(WrtDeviceApis::Commons::PlatformException, "Couldn't set-up watch functions.");
71 if (!dbus_connection_add_filter(m_connection, filterMessage, this, NULL)) {
72 ThrowMsg(WrtDeviceApis::Commons::PlatformException,
73 "Couldn't set signal handler callback.");
79 void Connection::close()
82 if (!m_connected) { return; }
84 dbus_connection_remove_filter(m_connection, filterMessage, this);
86 // Calls removeWatch() automagically.
87 if (!dbus_connection_set_watch_functions(m_connection, NULL, 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());
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();
105 removeFilterInternal(*it);
108 dbus_connection_unref(m_connection);
113 void Connection::addFilter(const std::string& rule)
116 addFilterInternal(rule);
118 m_filters.insert(rule);
121 void Connection::removeFilter(const std::string& rule)
124 removeFilterInternal(rule);
126 m_filters.erase(rule);
129 void Connection::setWorkerThread(DPL::Thread* thread)
131 assert(!m_connected && "Connection has already been established.");
132 m_messageDispatcher->SwitchToThread(thread);
135 void Connection::addFilterInternal(const std::string& rule)
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);
146 void Connection::removeFilterInternal(const std::string& rule)
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);
158 dbus_bool_t Connection::addWatch(DBusWatch* watch,
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(),
169 DPL::Mutex::ScopedLock lock(&this_->m_watchesMutex);
170 this_->m_watches[wrapper->getHandle()] = wrapper;
174 void Connection::removeWatch(DBusWatch* watch,
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(),
185 DPL::Mutex::ScopedLock lock(&this_->m_watchesMutex);
186 this_->m_watches.erase(wrapper.getHandle());
189 void Connection::toggleWatch(DBusWatch* watch,
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(),
200 this_->m_messageDispatcher->removeDescriptor(wrapper.getHandle(),
205 DBusHandlerResult Connection::filterMessage(DBusConnection* connection,
206 DBusMessage* message,
211 Connection* this_ = static_cast<Connection*>(data);
215 "DBUS message received from interface: " <<
216 dbus_message_get_interface(message));
217 msg.Reset(new Message(message));
219 LogError("DBus message not set, this should not happen!");
220 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
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
227 if ((*it).find(msg->getInterface()) != std::string::npos) {
228 MessageEvent event(msg);
229 this_->EmitEvent(event);
233 LogError("User-data not set, this should not happen!");
235 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
238 Connection::MessageDispatcher::MessageDispatcher(Connection* connection) :
239 m_connection(connection)
241 assert(m_connection && "Connection cannot be NULL.");
242 DPL::Event::ControllerEventHandler<AddDescriptorSyncEvent>::Touch();
243 DPL::Event::ControllerEventHandler<RemoveDescriptorSyncEvent>::Touch();
246 Connection::MessageDispatcher::~MessageDispatcher()
248 SwitchToThread(NULL);
251 void Connection::MessageDispatcher::addDescriptor(DPL::WaitableHandle handle,
252 DPL::WaitMode::Type mode)
255 DPL::WaitableEvent wait;
256 AddDescriptorSyncEvent event(handle, mode, &wait);
257 DPL::Event::ControllerEventHandler<AddDescriptorSyncEvent>::PostEvent(event);
258 DPL::WaitForSingleHandle(wait.GetHandle());
261 void Connection::MessageDispatcher::removeDescriptor(DPL::WaitableHandle handle,
262 DPL::WaitMode::Type mode)
265 DPL::WaitableEvent wait;
266 RemoveDescriptorSyncEvent event(handle, mode, &wait);
267 DPL::Event::ControllerEventHandler<RemoveDescriptorSyncEvent>::PostEvent(event);
268 DPL::WaitForSingleHandle(wait.GetHandle());
271 void Connection::MessageDispatcher::OnEventReceived(
272 const AddDescriptorSyncEvent& event)
275 DPL::WaitableHandleWatchSupport::InheritedContext()->AddWaitableHandleWatch(
279 event.GetArg2()->Signal();
282 void Connection::MessageDispatcher::OnEventReceived(
283 const RemoveDescriptorSyncEvent& event)
286 DPL::WaitableHandleWatchSupport::InheritedContext()->
287 RemoveWaitableHandleWatch(this, event.GetArg0(), event.GetArg1());
288 event.GetArg2()->Signal();
291 void Connection::MessageDispatcher::OnWaitableHandleEvent(
292 DPL::WaitableHandle waitableHandle,
293 DPL::WaitMode::Type mode)
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()) {
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
308 // TODO: not sure if there is point in dispatching connection when watch not found?
310 dbus_connection_ref(m_connection->m_connection);
311 while (dbus_connection_dispatch(m_connection->m_connection) ==
312 DBUS_DISPATCH_DATA_REMAINS) {
314 dbus_connection_unref(m_connection->m_connection);
317 Connection::Watch::Watch(DBusWatch* watch) : m_watch(watch)
319 assert(m_watch && "Watch cannot be NULL.");
322 DPL::WaitableHandle Connection::Watch::getHandle() const
324 return dbus_watch_get_unix_fd(m_watch);
327 DPL::WaitMode::Type Connection::Watch::getType() const
329 if ((dbus_watch_get_flags(m_watch) & DBUS_WATCH_WRITABLE) != 0) {
330 return DPL::WaitMode::Write;
332 return DPL::WaitMode::Read;
335 bool Connection::Watch::isEnabled() const
337 return (TRUE == dbus_watch_get_enabled(m_watch));
340 DBusWatch* Connection::Watch::get() const