diff -r 000000000000 -r 6474c204b198 ipc/dbus/RawDBusConnection.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ipc/dbus/RawDBusConnection.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,382 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include "base/message_loop.h" +#include "nsThreadUtils.h" +#include "RawDBusConnection.h" + +#ifdef CHROMIUM_LOG +#undef CHROMIUM_LOG +#endif + +#if defined(MOZ_WIDGET_GONK) +#include +#define CHROMIUM_LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gonk", args); +#else +#define CHROMIUM_LOG(args...) printf(args); +#endif + +namespace mozilla { +namespace ipc { + +// +// DBusWatcher +// + +class DBusWatcher : public MessageLoopForIO::Watcher +{ +public: + DBusWatcher(RawDBusConnection* aConnection, DBusWatch* aWatch) + : mConnection(aConnection), + mWatch(aWatch) + { + MOZ_ASSERT(mConnection); + MOZ_ASSERT(mWatch); + } + + ~DBusWatcher() + { } + + void StartWatching(); + void StopWatching(); + + static void FreeFunction(void* aData); + static dbus_bool_t AddWatchFunction(DBusWatch* aWatch, void* aData); + static void RemoveWatchFunction(DBusWatch* aWatch, void* aData); + static void ToggleWatchFunction(DBusWatch* aWatch, void* aData); + + RawDBusConnection* GetConnection(); + +private: + void OnFileCanReadWithoutBlocking(int aFd); + void OnFileCanWriteWithoutBlocking(int aFd); + + // Read watcher for libevent. Only to be accessed on IO Thread. + MessageLoopForIO::FileDescriptorWatcher mReadWatcher; + + // Write watcher for libevent. Only to be accessed on IO Thread. + MessageLoopForIO::FileDescriptorWatcher mWriteWatcher; + + // DBus structures + RawDBusConnection* mConnection; + DBusWatch* mWatch; +}; + +RawDBusConnection* +DBusWatcher::GetConnection() +{ + return mConnection; +} + +void DBusWatcher::StartWatching() +{ + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(mWatch); + + int fd = dbus_watch_get_unix_fd(mWatch); + + MessageLoopForIO* ioLoop = MessageLoopForIO::current(); + + unsigned int flags = dbus_watch_get_flags(mWatch); + + if (flags & DBUS_WATCH_READABLE) { + ioLoop->WatchFileDescriptor(fd, true, MessageLoopForIO::WATCH_READ, + &mReadWatcher, this); + } + if (flags & DBUS_WATCH_WRITABLE) { + ioLoop->WatchFileDescriptor(fd, true, MessageLoopForIO::WATCH_WRITE, + &mWriteWatcher, this); + } +} + +void DBusWatcher::StopWatching() +{ + MOZ_ASSERT(!NS_IsMainThread()); + + unsigned int flags = dbus_watch_get_flags(mWatch); + + if (flags & DBUS_WATCH_READABLE) { + mReadWatcher.StopWatchingFileDescriptor(); + } + if (flags & DBUS_WATCH_WRITABLE) { + mWriteWatcher.StopWatchingFileDescriptor(); + } +} + +// DBus utility functions, used as function pointers in DBus setup + +void +DBusWatcher::FreeFunction(void* aData) +{ + delete static_cast(aData); +} + +dbus_bool_t +DBusWatcher::AddWatchFunction(DBusWatch* aWatch, void* aData) +{ + MOZ_ASSERT(!NS_IsMainThread()); + + RawDBusConnection* connection = static_cast(aData); + + DBusWatcher* dbusWatcher = new DBusWatcher(connection, aWatch); + dbus_watch_set_data(aWatch, dbusWatcher, DBusWatcher::FreeFunction); + + if (dbus_watch_get_enabled(aWatch)) { + dbusWatcher->StartWatching(); + } + + return TRUE; +} + +void +DBusWatcher::RemoveWatchFunction(DBusWatch* aWatch, void* aData) +{ + MOZ_ASSERT(!NS_IsMainThread()); + + DBusWatcher* dbusWatcher = + static_cast(dbus_watch_get_data(aWatch)); + dbusWatcher->StopWatching(); +} + +void +DBusWatcher::ToggleWatchFunction(DBusWatch* aWatch, void* aData) +{ + MOZ_ASSERT(!NS_IsMainThread()); + + DBusWatcher* dbusWatcher = + static_cast(dbus_watch_get_data(aWatch)); + + if (dbus_watch_get_enabled(aWatch)) { + dbusWatcher->StartWatching(); + } else { + dbusWatcher->StopWatching(); + } +} + +// I/O-loop callbacks + +void +DBusWatcher::OnFileCanReadWithoutBlocking(int aFd) +{ + MOZ_ASSERT(!NS_IsMainThread()); + + dbus_watch_handle(mWatch, DBUS_WATCH_READABLE); + + DBusDispatchStatus dbusDispatchStatus; + do { + dbusDispatchStatus = + dbus_connection_dispatch(mConnection->GetConnection()); + } while (dbusDispatchStatus == DBUS_DISPATCH_DATA_REMAINS); +} + +void +DBusWatcher::OnFileCanWriteWithoutBlocking(int aFd) +{ + MOZ_ASSERT(!NS_IsMainThread()); + + dbus_watch_handle(mWatch, DBUS_WATCH_WRITABLE); +} + +// +// Notification +// + +class Notification +{ +public: + Notification(DBusReplyCallback aCallback, void* aData) + : mCallback(aCallback), + mData(aData) + { } + + // Callback function for DBus replies. Only run it on I/O thread. + // + static void Handle(DBusPendingCall* aCall, void* aData) + { + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(MessageLoop::current()); + + nsAutoPtr ntfn(static_cast(aData)); + + // The reply can be non-null if the timeout has been reached. + DBusMessage* reply = dbus_pending_call_steal_reply(aCall); + + if (reply) { + ntfn->RunCallback(reply); + dbus_message_unref(reply); + } + + dbus_pending_call_cancel(aCall); + dbus_pending_call_unref(aCall); + } + +private: + void RunCallback(DBusMessage* aMessage) + { + if (mCallback) { + mCallback(aMessage, mData); + } + } + + DBusReplyCallback mCallback; + void* mData; +}; + +// +// RawDBusConnection +// + +bool RawDBusConnection::sDBusIsInit(false); + +RawDBusConnection::RawDBusConnection() +{ +} + +RawDBusConnection::~RawDBusConnection() +{ +} + +nsresult RawDBusConnection::EstablishDBusConnection() +{ + if (!sDBusIsInit) { + dbus_bool_t success = dbus_threads_init_default(); + NS_ENSURE_TRUE(success == TRUE, NS_ERROR_FAILURE); + sDBusIsInit = true; + } + DBusError err; + dbus_error_init(&err); + mConnection = dbus_bus_get_private(DBUS_BUS_SYSTEM, &err); + if (dbus_error_is_set(&err)) { + dbus_error_free(&err); + return NS_ERROR_FAILURE; + } + dbus_connection_set_exit_on_disconnect(mConnection, FALSE); + return NS_OK; +} + +bool RawDBusConnection::Watch() +{ + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(MessageLoop::current()); + + dbus_bool_t success = + dbus_connection_set_watch_functions(mConnection, + DBusWatcher::AddWatchFunction, + DBusWatcher::RemoveWatchFunction, + DBusWatcher::ToggleWatchFunction, + this, nullptr); + NS_ENSURE_TRUE(success == TRUE, false); + + return true; +} + +void RawDBusConnection::ScopedDBusConnectionPtrTraits::release(DBusConnection* ptr) +{ + if (ptr) { + dbus_connection_close(ptr); + dbus_connection_unref(ptr); + } +} + +bool RawDBusConnection::Send(DBusMessage* aMessage) +{ + MOZ_ASSERT(aMessage); + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(MessageLoop::current()); + + dbus_bool_t success = dbus_connection_send(mConnection, + aMessage, + nullptr); + if (success != TRUE) { + dbus_message_unref(aMessage); + return false; + } + return true; +} + +bool RawDBusConnection::SendWithReply(DBusReplyCallback aCallback, + void* aData, + int aTimeout, + DBusMessage* aMessage) +{ + MOZ_ASSERT(aMessage); + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(MessageLoop::current()); + + nsAutoPtr ntfn(new Notification(aCallback, aData)); + NS_ENSURE_TRUE(ntfn, false); + + DBusPendingCall* call; + + dbus_bool_t success = dbus_connection_send_with_reply(mConnection, + aMessage, + &call, + aTimeout); + NS_ENSURE_TRUE(success == TRUE, false); + + success = dbus_pending_call_set_notify(call, Notification::Handle, + ntfn, nullptr); + NS_ENSURE_TRUE(success == TRUE, false); + + ntfn.forget(); + dbus_message_unref(aMessage); + + return true; +} + +bool RawDBusConnection::SendWithReply(DBusReplyCallback aCallback, + void* aData, + int aTimeout, + const char* aDestination, + const char* aPath, + const char* aIntf, + const char* aFunc, + int aFirstArgType, + ...) +{ + MOZ_ASSERT(!NS_IsMainThread()); + va_list args; + + va_start(args, aFirstArgType); + DBusMessage* msg = BuildDBusMessage(aDestination, aPath, aIntf, aFunc, + aFirstArgType, args); + va_end(args); + + if (!msg) { + return false; + } + + return SendWithReply(aCallback, aData, aTimeout, msg); +} + +DBusMessage* RawDBusConnection::BuildDBusMessage(const char* aDestination, + const char* aPath, + const char* aIntf, + const char* aFunc, + int aFirstArgType, + va_list aArgs) +{ + DBusMessage* msg = dbus_message_new_method_call(aDestination, + aPath, aIntf, + aFunc); + if (!msg) { + CHROMIUM_LOG("Could not allocate D-Bus message object!"); + return nullptr; + } + + /* append arguments */ + if (!dbus_message_append_args_valist(msg, aFirstArgType, aArgs)) { + CHROMIUM_LOG("Could not append argument to method call!"); + dbus_message_unref(msg); + return nullptr; + } + + return msg; +} + +} +}