michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* vim:expandtab:shiftwidth=4:tabstop=4: michael@0: */ 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 michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsDBusService.h" michael@0: #include "nsComponentManagerUtils.h" michael@0: #include "nsAutoPtr.h" michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: nsDBusService::nsDBusService() { michael@0: mConnection = nullptr; michael@0: mSingleClient = nullptr; michael@0: } michael@0: michael@0: nsDBusService::~nsDBusService() { michael@0: NS_ASSERTION(!mSingleClient, "Client failed to unregister"); michael@0: DropConnection(); michael@0: if (mReconnectTimer) { michael@0: mReconnectTimer->Cancel(); michael@0: } michael@0: gSingleton = nullptr; michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(nsDBusService, nsDBusService) michael@0: michael@0: nsDBusService* nsDBusService::gSingleton = nullptr; michael@0: michael@0: already_AddRefed michael@0: nsDBusService::Get() { michael@0: if (!gSingleton) { michael@0: gSingleton = new nsDBusService(); michael@0: } michael@0: nsRefPtr ret = gSingleton; michael@0: return ret.forget(); michael@0: } michael@0: michael@0: nsresult michael@0: nsDBusService::AddClient(DBusClient* client) { michael@0: NS_ASSERTION(!mSingleClient, "Only one client supported right now"); michael@0: mSingleClient = client; michael@0: nsresult rv = CreateConnection(); michael@0: if (NS_FAILED(rv)) { michael@0: mSingleClient = nullptr; michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: void michael@0: nsDBusService::RemoveClient(DBusClient* client) { michael@0: NS_ASSERTION(mSingleClient == client, "Removing wrong client"); michael@0: mSingleClient = nullptr; michael@0: } michael@0: michael@0: DBusPendingCall* michael@0: nsDBusService::SendWithReply(DBusClient* client, DBusMessage* message) { michael@0: DBusPendingCall* reply = nullptr; michael@0: if (mConnection) { michael@0: if (!dbus_connection_send_with_reply(mConnection, message, &reply, -1)) { michael@0: reply = nullptr; michael@0: } michael@0: } michael@0: dbus_message_unref(message); michael@0: return reply; michael@0: } michael@0: michael@0: bool nsDBusService::HandleMessage(DBusMessage* message) { michael@0: if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, michael@0: "Disconnected")) { michael@0: HandleDBusDisconnect(); michael@0: return false; michael@0: } michael@0: michael@0: return mSingleClient && mSingleClient->HandleMessage(message); michael@0: } michael@0: michael@0: static DBusHandlerResult dbus_filter(DBusConnection* connection, michael@0: DBusMessage* message, michael@0: void* user_data) { michael@0: return static_cast(user_data)->HandleMessage(message) michael@0: ? DBUS_HANDLER_RESULT_HANDLED : DBUS_HANDLER_RESULT_NOT_YET_HANDLED; michael@0: } michael@0: michael@0: void nsDBusService::DoTimerCallback(nsITimer *aTimer) { michael@0: if (aTimer == mReconnectTimer.get()) { michael@0: nsresult rv = CreateConnection(); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: mReconnectTimer->Cancel(); michael@0: mReconnectTimer = nullptr; michael@0: } michael@0: } michael@0: } michael@0: michael@0: static void TimerCallback(nsITimer *aTimer, void *aClosure) { michael@0: static_cast(aClosure)->DoTimerCallback(aTimer); michael@0: } michael@0: michael@0: void nsDBusService::DropConnection() { michael@0: if (mConnection) { michael@0: dbus_connection_remove_filter(mConnection, dbus_filter, this); michael@0: if (mSingleClient) { michael@0: mSingleClient->UnregisterWithConnection(mConnection); michael@0: } michael@0: dbus_connection_unref(mConnection); michael@0: mConnection = nullptr; michael@0: } michael@0: } michael@0: michael@0: void nsDBusService::HandleDBusDisconnect() { michael@0: DropConnection(); michael@0: michael@0: nsresult rv; michael@0: mReconnectTimer = do_CreateInstance("@mozilla.org/timer;1", &rv); michael@0: if (NS_FAILED(rv)) michael@0: return; michael@0: rv = mReconnectTimer->InitWithFuncCallback(TimerCallback, this, michael@0: 5000, nsITimer::TYPE_REPEATING_SLACK); michael@0: if (NS_FAILED(rv)) { michael@0: mReconnectTimer = nullptr; michael@0: return; michael@0: } michael@0: } michael@0: michael@0: nsresult nsDBusService::CreateConnection() { michael@0: mConnection = dbus_bus_get(DBUS_BUS_SYSTEM, nullptr); michael@0: if (!mConnection) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: dbus_connection_set_exit_on_disconnect(mConnection, false); michael@0: dbus_connection_setup_with_g_main(mConnection, nullptr); michael@0: michael@0: if (!dbus_connection_add_filter(mConnection, dbus_filter, this, nullptr)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: mSingleClient->RegisterWithConnection(mConnection); michael@0: return NS_OK; michael@0: }