1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/ipc/dbus/RawDBusConnection.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,382 @@ 1.4 +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ 1.5 +/* vim: set ts=2 et sw=2 tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.8 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include <dbus/dbus.h> 1.11 +#include "base/message_loop.h" 1.12 +#include "nsThreadUtils.h" 1.13 +#include "RawDBusConnection.h" 1.14 + 1.15 +#ifdef CHROMIUM_LOG 1.16 +#undef CHROMIUM_LOG 1.17 +#endif 1.18 + 1.19 +#if defined(MOZ_WIDGET_GONK) 1.20 +#include <android/log.h> 1.21 +#define CHROMIUM_LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gonk", args); 1.22 +#else 1.23 +#define CHROMIUM_LOG(args...) printf(args); 1.24 +#endif 1.25 + 1.26 +namespace mozilla { 1.27 +namespace ipc { 1.28 + 1.29 +// 1.30 +// DBusWatcher 1.31 +// 1.32 + 1.33 +class DBusWatcher : public MessageLoopForIO::Watcher 1.34 +{ 1.35 +public: 1.36 + DBusWatcher(RawDBusConnection* aConnection, DBusWatch* aWatch) 1.37 + : mConnection(aConnection), 1.38 + mWatch(aWatch) 1.39 + { 1.40 + MOZ_ASSERT(mConnection); 1.41 + MOZ_ASSERT(mWatch); 1.42 + } 1.43 + 1.44 + ~DBusWatcher() 1.45 + { } 1.46 + 1.47 + void StartWatching(); 1.48 + void StopWatching(); 1.49 + 1.50 + static void FreeFunction(void* aData); 1.51 + static dbus_bool_t AddWatchFunction(DBusWatch* aWatch, void* aData); 1.52 + static void RemoveWatchFunction(DBusWatch* aWatch, void* aData); 1.53 + static void ToggleWatchFunction(DBusWatch* aWatch, void* aData); 1.54 + 1.55 + RawDBusConnection* GetConnection(); 1.56 + 1.57 +private: 1.58 + void OnFileCanReadWithoutBlocking(int aFd); 1.59 + void OnFileCanWriteWithoutBlocking(int aFd); 1.60 + 1.61 + // Read watcher for libevent. Only to be accessed on IO Thread. 1.62 + MessageLoopForIO::FileDescriptorWatcher mReadWatcher; 1.63 + 1.64 + // Write watcher for libevent. Only to be accessed on IO Thread. 1.65 + MessageLoopForIO::FileDescriptorWatcher mWriteWatcher; 1.66 + 1.67 + // DBus structures 1.68 + RawDBusConnection* mConnection; 1.69 + DBusWatch* mWatch; 1.70 +}; 1.71 + 1.72 +RawDBusConnection* 1.73 +DBusWatcher::GetConnection() 1.74 +{ 1.75 + return mConnection; 1.76 +} 1.77 + 1.78 +void DBusWatcher::StartWatching() 1.79 +{ 1.80 + MOZ_ASSERT(!NS_IsMainThread()); 1.81 + MOZ_ASSERT(mWatch); 1.82 + 1.83 + int fd = dbus_watch_get_unix_fd(mWatch); 1.84 + 1.85 + MessageLoopForIO* ioLoop = MessageLoopForIO::current(); 1.86 + 1.87 + unsigned int flags = dbus_watch_get_flags(mWatch); 1.88 + 1.89 + if (flags & DBUS_WATCH_READABLE) { 1.90 + ioLoop->WatchFileDescriptor(fd, true, MessageLoopForIO::WATCH_READ, 1.91 + &mReadWatcher, this); 1.92 + } 1.93 + if (flags & DBUS_WATCH_WRITABLE) { 1.94 + ioLoop->WatchFileDescriptor(fd, true, MessageLoopForIO::WATCH_WRITE, 1.95 + &mWriteWatcher, this); 1.96 + } 1.97 +} 1.98 + 1.99 +void DBusWatcher::StopWatching() 1.100 +{ 1.101 + MOZ_ASSERT(!NS_IsMainThread()); 1.102 + 1.103 + unsigned int flags = dbus_watch_get_flags(mWatch); 1.104 + 1.105 + if (flags & DBUS_WATCH_READABLE) { 1.106 + mReadWatcher.StopWatchingFileDescriptor(); 1.107 + } 1.108 + if (flags & DBUS_WATCH_WRITABLE) { 1.109 + mWriteWatcher.StopWatchingFileDescriptor(); 1.110 + } 1.111 +} 1.112 + 1.113 +// DBus utility functions, used as function pointers in DBus setup 1.114 + 1.115 +void 1.116 +DBusWatcher::FreeFunction(void* aData) 1.117 +{ 1.118 + delete static_cast<DBusWatcher*>(aData); 1.119 +} 1.120 + 1.121 +dbus_bool_t 1.122 +DBusWatcher::AddWatchFunction(DBusWatch* aWatch, void* aData) 1.123 +{ 1.124 + MOZ_ASSERT(!NS_IsMainThread()); 1.125 + 1.126 + RawDBusConnection* connection = static_cast<RawDBusConnection*>(aData); 1.127 + 1.128 + DBusWatcher* dbusWatcher = new DBusWatcher(connection, aWatch); 1.129 + dbus_watch_set_data(aWatch, dbusWatcher, DBusWatcher::FreeFunction); 1.130 + 1.131 + if (dbus_watch_get_enabled(aWatch)) { 1.132 + dbusWatcher->StartWatching(); 1.133 + } 1.134 + 1.135 + return TRUE; 1.136 +} 1.137 + 1.138 +void 1.139 +DBusWatcher::RemoveWatchFunction(DBusWatch* aWatch, void* aData) 1.140 +{ 1.141 + MOZ_ASSERT(!NS_IsMainThread()); 1.142 + 1.143 + DBusWatcher* dbusWatcher = 1.144 + static_cast<DBusWatcher*>(dbus_watch_get_data(aWatch)); 1.145 + dbusWatcher->StopWatching(); 1.146 +} 1.147 + 1.148 +void 1.149 +DBusWatcher::ToggleWatchFunction(DBusWatch* aWatch, void* aData) 1.150 +{ 1.151 + MOZ_ASSERT(!NS_IsMainThread()); 1.152 + 1.153 + DBusWatcher* dbusWatcher = 1.154 + static_cast<DBusWatcher*>(dbus_watch_get_data(aWatch)); 1.155 + 1.156 + if (dbus_watch_get_enabled(aWatch)) { 1.157 + dbusWatcher->StartWatching(); 1.158 + } else { 1.159 + dbusWatcher->StopWatching(); 1.160 + } 1.161 +} 1.162 + 1.163 +// I/O-loop callbacks 1.164 + 1.165 +void 1.166 +DBusWatcher::OnFileCanReadWithoutBlocking(int aFd) 1.167 +{ 1.168 + MOZ_ASSERT(!NS_IsMainThread()); 1.169 + 1.170 + dbus_watch_handle(mWatch, DBUS_WATCH_READABLE); 1.171 + 1.172 + DBusDispatchStatus dbusDispatchStatus; 1.173 + do { 1.174 + dbusDispatchStatus = 1.175 + dbus_connection_dispatch(mConnection->GetConnection()); 1.176 + } while (dbusDispatchStatus == DBUS_DISPATCH_DATA_REMAINS); 1.177 +} 1.178 + 1.179 +void 1.180 +DBusWatcher::OnFileCanWriteWithoutBlocking(int aFd) 1.181 +{ 1.182 + MOZ_ASSERT(!NS_IsMainThread()); 1.183 + 1.184 + dbus_watch_handle(mWatch, DBUS_WATCH_WRITABLE); 1.185 +} 1.186 + 1.187 +// 1.188 +// Notification 1.189 +// 1.190 + 1.191 +class Notification 1.192 +{ 1.193 +public: 1.194 + Notification(DBusReplyCallback aCallback, void* aData) 1.195 + : mCallback(aCallback), 1.196 + mData(aData) 1.197 + { } 1.198 + 1.199 + // Callback function for DBus replies. Only run it on I/O thread. 1.200 + // 1.201 + static void Handle(DBusPendingCall* aCall, void* aData) 1.202 + { 1.203 + MOZ_ASSERT(!NS_IsMainThread()); 1.204 + MOZ_ASSERT(MessageLoop::current()); 1.205 + 1.206 + nsAutoPtr<Notification> ntfn(static_cast<Notification*>(aData)); 1.207 + 1.208 + // The reply can be non-null if the timeout has been reached. 1.209 + DBusMessage* reply = dbus_pending_call_steal_reply(aCall); 1.210 + 1.211 + if (reply) { 1.212 + ntfn->RunCallback(reply); 1.213 + dbus_message_unref(reply); 1.214 + } 1.215 + 1.216 + dbus_pending_call_cancel(aCall); 1.217 + dbus_pending_call_unref(aCall); 1.218 + } 1.219 + 1.220 +private: 1.221 + void RunCallback(DBusMessage* aMessage) 1.222 + { 1.223 + if (mCallback) { 1.224 + mCallback(aMessage, mData); 1.225 + } 1.226 + } 1.227 + 1.228 + DBusReplyCallback mCallback; 1.229 + void* mData; 1.230 +}; 1.231 + 1.232 +// 1.233 +// RawDBusConnection 1.234 +// 1.235 + 1.236 +bool RawDBusConnection::sDBusIsInit(false); 1.237 + 1.238 +RawDBusConnection::RawDBusConnection() 1.239 +{ 1.240 +} 1.241 + 1.242 +RawDBusConnection::~RawDBusConnection() 1.243 +{ 1.244 +} 1.245 + 1.246 +nsresult RawDBusConnection::EstablishDBusConnection() 1.247 +{ 1.248 + if (!sDBusIsInit) { 1.249 + dbus_bool_t success = dbus_threads_init_default(); 1.250 + NS_ENSURE_TRUE(success == TRUE, NS_ERROR_FAILURE); 1.251 + sDBusIsInit = true; 1.252 + } 1.253 + DBusError err; 1.254 + dbus_error_init(&err); 1.255 + mConnection = dbus_bus_get_private(DBUS_BUS_SYSTEM, &err); 1.256 + if (dbus_error_is_set(&err)) { 1.257 + dbus_error_free(&err); 1.258 + return NS_ERROR_FAILURE; 1.259 + } 1.260 + dbus_connection_set_exit_on_disconnect(mConnection, FALSE); 1.261 + return NS_OK; 1.262 +} 1.263 + 1.264 +bool RawDBusConnection::Watch() 1.265 +{ 1.266 + MOZ_ASSERT(!NS_IsMainThread()); 1.267 + MOZ_ASSERT(MessageLoop::current()); 1.268 + 1.269 + dbus_bool_t success = 1.270 + dbus_connection_set_watch_functions(mConnection, 1.271 + DBusWatcher::AddWatchFunction, 1.272 + DBusWatcher::RemoveWatchFunction, 1.273 + DBusWatcher::ToggleWatchFunction, 1.274 + this, nullptr); 1.275 + NS_ENSURE_TRUE(success == TRUE, false); 1.276 + 1.277 + return true; 1.278 +} 1.279 + 1.280 +void RawDBusConnection::ScopedDBusConnectionPtrTraits::release(DBusConnection* ptr) 1.281 +{ 1.282 + if (ptr) { 1.283 + dbus_connection_close(ptr); 1.284 + dbus_connection_unref(ptr); 1.285 + } 1.286 +} 1.287 + 1.288 +bool RawDBusConnection::Send(DBusMessage* aMessage) 1.289 +{ 1.290 + MOZ_ASSERT(aMessage); 1.291 + MOZ_ASSERT(!NS_IsMainThread()); 1.292 + MOZ_ASSERT(MessageLoop::current()); 1.293 + 1.294 + dbus_bool_t success = dbus_connection_send(mConnection, 1.295 + aMessage, 1.296 + nullptr); 1.297 + if (success != TRUE) { 1.298 + dbus_message_unref(aMessage); 1.299 + return false; 1.300 + } 1.301 + return true; 1.302 +} 1.303 + 1.304 +bool RawDBusConnection::SendWithReply(DBusReplyCallback aCallback, 1.305 + void* aData, 1.306 + int aTimeout, 1.307 + DBusMessage* aMessage) 1.308 +{ 1.309 + MOZ_ASSERT(aMessage); 1.310 + MOZ_ASSERT(!NS_IsMainThread()); 1.311 + MOZ_ASSERT(MessageLoop::current()); 1.312 + 1.313 + nsAutoPtr<Notification> ntfn(new Notification(aCallback, aData)); 1.314 + NS_ENSURE_TRUE(ntfn, false); 1.315 + 1.316 + DBusPendingCall* call; 1.317 + 1.318 + dbus_bool_t success = dbus_connection_send_with_reply(mConnection, 1.319 + aMessage, 1.320 + &call, 1.321 + aTimeout); 1.322 + NS_ENSURE_TRUE(success == TRUE, false); 1.323 + 1.324 + success = dbus_pending_call_set_notify(call, Notification::Handle, 1.325 + ntfn, nullptr); 1.326 + NS_ENSURE_TRUE(success == TRUE, false); 1.327 + 1.328 + ntfn.forget(); 1.329 + dbus_message_unref(aMessage); 1.330 + 1.331 + return true; 1.332 +} 1.333 + 1.334 +bool RawDBusConnection::SendWithReply(DBusReplyCallback aCallback, 1.335 + void* aData, 1.336 + int aTimeout, 1.337 + const char* aDestination, 1.338 + const char* aPath, 1.339 + const char* aIntf, 1.340 + const char* aFunc, 1.341 + int aFirstArgType, 1.342 + ...) 1.343 +{ 1.344 + MOZ_ASSERT(!NS_IsMainThread()); 1.345 + va_list args; 1.346 + 1.347 + va_start(args, aFirstArgType); 1.348 + DBusMessage* msg = BuildDBusMessage(aDestination, aPath, aIntf, aFunc, 1.349 + aFirstArgType, args); 1.350 + va_end(args); 1.351 + 1.352 + if (!msg) { 1.353 + return false; 1.354 + } 1.355 + 1.356 + return SendWithReply(aCallback, aData, aTimeout, msg); 1.357 +} 1.358 + 1.359 +DBusMessage* RawDBusConnection::BuildDBusMessage(const char* aDestination, 1.360 + const char* aPath, 1.361 + const char* aIntf, 1.362 + const char* aFunc, 1.363 + int aFirstArgType, 1.364 + va_list aArgs) 1.365 +{ 1.366 + DBusMessage* msg = dbus_message_new_method_call(aDestination, 1.367 + aPath, aIntf, 1.368 + aFunc); 1.369 + if (!msg) { 1.370 + CHROMIUM_LOG("Could not allocate D-Bus message object!"); 1.371 + return nullptr; 1.372 + } 1.373 + 1.374 + /* append arguments */ 1.375 + if (!dbus_message_append_args_valist(msg, aFirstArgType, aArgs)) { 1.376 + CHROMIUM_LOG("Could not append argument to method call!"); 1.377 + dbus_message_unref(msg); 1.378 + return nullptr; 1.379 + } 1.380 + 1.381 + return msg; 1.382 +} 1.383 + 1.384 +} 1.385 +}