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