diff --git a/configure.in b/configure.in index 46f73aecec3..2974616486b 100644 --- a/configure.in +++ b/configure.in @@ -291,7 +291,7 @@ if test -n "$gonkdir" ; then STLPORT_CPPFLAGS="-I$gonkdir/ndk/sources/cxx-stl/stlport/stlport/" STLPORT_LIBS="-lstlport" - CPPFLAGS="-DANDROID -isystem $gonkdir/bionic/libc/include/ -isystem $gonkdir/bionic/libc/kernel/common -isystem $gonkdir/bionic/libc/arch-arm/include -isystem $gonkdir/bionic/libc/kernel/arch-arm -isystem $gonkdir/bionic/libm/include -I$gonkdir/frameworks/base/opengl/include -I$gonkdir/frameworks/base/native/include -I$gonkdir/hardware/libhardware/include -I$gonkdir/hardware/libhardware_legacy/include -I$gonkdir/system/core/include -isystem $gonkdir/bionic -I$gonkdir/frameworks/base/include $CPPFLAGS -I$gonkdir/frameworks/base/services/sensorservice" + CPPFLAGS="-DANDROID -isystem $gonkdir/bionic/libc/include/ -isystem $gonkdir/bionic/libc/kernel/common -isystem $gonkdir/bionic/libc/arch-arm/include -isystem $gonkdir/bionic/libc/kernel/arch-arm -isystem $gonkdir/bionic/libm/include -I$gonkdir/frameworks/base/opengl/include -I$gonkdir/frameworks/base/native/include -I$gonkdir/hardware/libhardware/include -I$gonkdir/hardware/libhardware_legacy/include -I$gonkdir/system/core/include -isystem $gonkdir/bionic -I$gonkdir/frameworks/base/include -I$gonkdir/external/dbus $CPPFLAGS -I$gonkdir/frameworks/base/services/sensorservice" CFLAGS="-mandroid -fno-short-enums -fno-exceptions $CFLAGS" CXXFLAGS="-mandroid -fno-short-enums -fno-exceptions $CXXFLAGS $STLPORT_CPPFLAGS" LIBS="$LIBS $STLPORT_LIBS" diff --git a/dom/bluetooth/BluetoothAdapter.cpp b/dom/bluetooth/BluetoothAdapter.cpp index 424d4a7b025..174d364ac6f 100644 --- a/dom/bluetooth/BluetoothAdapter.cpp +++ b/dom/bluetooth/BluetoothAdapter.cpp @@ -5,6 +5,7 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "BluetoothAdapter.h" +#include "BluetoothFirmware.h" #include "nsDOMClassInfo.h" #include "nsDOMEvent.h" @@ -12,7 +13,6 @@ #include "nsXPCOMCIDInternal.h" #include "mozilla/LazyIdleThread.h" #include "mozilla/Util.h" -#include static void FireEnabled(bool aResult, nsIDOMDOMRequest* aDomRequest) @@ -34,56 +34,6 @@ FireEnabled(bool aResult, nsIDOMDOMRequest* aDomRequest) USING_BLUETOOTH_NAMESPACE -static struct BluedroidFunctions { - bool initialized; - bool tried_initialization; - - BluedroidFunctions() : - initialized(false), - tried_initialization(false) - { - } - - int (* bt_enable)(); - int (* bt_disable)(); - int (* bt_is_enabled)(); -} sBluedroidFunctions; - -static bool EnsureBluetoothInit() { - if (sBluedroidFunctions.tried_initialization) - { - return sBluedroidFunctions.initialized; - } - - sBluedroidFunctions.initialized = false; - sBluedroidFunctions.tried_initialization = true; - - void* handle = dlopen("libbluedroid.so", RTLD_LAZY); - - if(!handle) { - NS_ERROR("Failed to open libbluedroid.so, bluetooth cannot run"); - return false; - } - - sBluedroidFunctions.bt_enable = (int (*)())dlsym(handle, "bt_enable"); - if(sBluedroidFunctions.bt_enable == NULL) { - NS_ERROR("Failed to attach bt_enable function"); - return false; - } - sBluedroidFunctions.bt_disable = (int (*)())dlsym(handle, "bt_disable"); - if(sBluedroidFunctions.bt_disable == NULL) { - NS_ERROR("Failed to attach bt_disable function"); - return false; - } - sBluedroidFunctions.bt_is_enabled = (int (*)())dlsym(handle, "bt_is_enabled"); - if(sBluedroidFunctions.bt_is_enabled == NULL) { - NS_ERROR("Failed to attach bt_is_enabled function"); - return false; - } - sBluedroidFunctions.initialized = true; - return true; -} - class ToggleBtResultTask : public nsRunnable { public: diff --git a/dom/bluetooth/BluetoothFirmware.cpp b/dom/bluetooth/BluetoothFirmware.cpp new file mode 100644 index 00000000000..633b7d7e9ca --- /dev/null +++ b/dom/bluetooth/BluetoothFirmware.cpp @@ -0,0 +1,54 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=40: */ +/* 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 "BluetoothFirmware.h" + +#include "nsDebug.h" +#include "nsError.h" +#include + +namespace mozilla { +namespace dom { +namespace bluetooth { + +bool EnsureBluetoothInit() { + if (sBluedroidFunctions.tried_initialization) + { + return sBluedroidFunctions.initialized; + } + + sBluedroidFunctions.initialized = false; + sBluedroidFunctions.tried_initialization = true; + + void* handle = dlopen("libbluedroid.so", RTLD_LAZY); + + if(!handle) { + NS_ERROR("Failed to open libbluedroid.so, bluetooth cannot run"); + return false; + } + + sBluedroidFunctions.bt_enable = (int (*)())dlsym(handle, "bt_enable"); + if(sBluedroidFunctions.bt_enable == NULL) { + NS_ERROR("Failed to attach bt_enable function"); + return false; + } + sBluedroidFunctions.bt_disable = (int (*)())dlsym(handle, "bt_disable"); + if(sBluedroidFunctions.bt_disable == NULL) { + NS_ERROR("Failed to attach bt_disable function"); + return false; + } + sBluedroidFunctions.bt_is_enabled = (int (*)())dlsym(handle, "bt_is_enabled"); + if(sBluedroidFunctions.bt_is_enabled == NULL) { + NS_ERROR("Failed to attach bt_is_enabled function"); + return false; + } + sBluedroidFunctions.initialized = true; + return true; +} + +} +} +} diff --git a/dom/bluetooth/BluetoothFirmware.h b/dom/bluetooth/BluetoothFirmware.h new file mode 100644 index 00000000000..071273a8142 --- /dev/null +++ b/dom/bluetooth/BluetoothFirmware.h @@ -0,0 +1,34 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=40: */ +/* 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/. */ + +#ifndef mozilla_dom_bluetooth_bluetoothfirmware_h__ +#define mozilla_dom_bluetooth_bluetoothfirmware_h__ + +namespace mozilla { +namespace dom { +namespace bluetooth { + +static struct BluedroidFunctions { + bool initialized; + bool tried_initialization; + + BluedroidFunctions() : + initialized(false), + tried_initialization(false) + { + } + + int (* bt_enable)(); + int (* bt_disable)(); + int (* bt_is_enabled)(); +} sBluedroidFunctions; + +bool EnsureBluetoothInit(); +} +} +} + +#endif diff --git a/dom/bluetooth/Makefile.in b/dom/bluetooth/Makefile.in index 7d782e5356c..f9f5b14d618 100644 --- a/dom/bluetooth/Makefile.in +++ b/dom/bluetooth/Makefile.in @@ -19,6 +19,7 @@ include $(topsrcdir)/dom/dom-config.mk CPPSRCS = \ BluetoothAdapter.cpp \ + BluetoothFirmware.cpp \ $(NULL) XPIDLSRCS = \ @@ -26,5 +27,17 @@ XPIDLSRCS = \ nsIDOMBluetoothAdapter.idl \ $(NULL) +ifneq (gonk,$(MOZ_WIDGET_TOOLKIT)) +CFLAGS += $(MOZ_DBUS_CFLAGS) +CXXFLAGS += $(MOZ_DBUS_CFLAGS) -DHAVE_PTHREADS +endif + include $(topsrcdir)/config/rules.mk +ifeq (Linux,$(OS_TARGET)) +ifdef MOZ_ENABLE_DBUS +CFLAGS += $(MOZ_DBUS_GLIB_CFLAGS) +CXXFLAGS += $(MOZ_DBUS_GLIB_CFLAGS) -DHAVE_PTHREADS +endif +endif + diff --git a/dom/system/gonk/Makefile.in b/dom/system/gonk/Makefile.in index 3779050924a..725a104a960 100644 --- a/dom/system/gonk/Makefile.in +++ b/dom/system/gonk/Makefile.in @@ -35,6 +35,7 @@ LOCAL_INCLUDES = \ -I$(topsrcdir)/dom/src/geolocation \ -I$(topsrcdir)/dom/telephony \ -I$(topsrcdir)/dom/wifi \ + -I$(topsrcdir)/dom/bluetooth \ -I$(topsrcdir)/content/events/src \ $(NULL) diff --git a/dom/system/gonk/SystemWorkerManager.cpp b/dom/system/gonk/SystemWorkerManager.cpp index 9f936576b29..86e3cc2f95f 100644 --- a/dom/system/gonk/SystemWorkerManager.cpp +++ b/dom/system/gonk/SystemWorkerManager.cpp @@ -49,15 +49,29 @@ #include "jsfriendapi.h" #include "mozilla/dom/workers/Workers.h" #include "mozilla/ipc/Ril.h" +#ifdef MOZ_B2G_BT +#include "mozilla/ipc/DBusThread.h" +#include "BluetoothFirmware.h" +#endif #include "nsContentUtils.h" #include "nsServiceManagerUtils.h" #include "nsThreadUtils.h" #include "nsRadioInterfaceLayer.h" #include "WifiWorker.h" +#undef LOG +#if defined(MOZ_WIDGET_GONK) +#include +#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GonkBluetooth", args); +#else +#define BTDEBUG true +#define LOG(args...) if(BTDEBUG) printf(args); +#endif + USING_WORKERS_NAMESPACE using namespace mozilla::dom::gonk; +using namespace mozilla::dom::bluetooth; using namespace mozilla::ipc; namespace { @@ -229,6 +243,11 @@ SystemWorkerManager::Init() rv = InitWifi(cx); NS_ENSURE_SUCCESS(rv, rv); +#ifdef MOZ_B2G_BT + rv = InitBluetooth(cx); + NS_ENSURE_SUCCESS(rv, rv); +#endif + nsCOMPtr obs = do_GetService(NS_OBSERVERSERVICE_CONTRACTID); if (!obs) { @@ -250,7 +269,9 @@ SystemWorkerManager::Shutdown() mShutdown = true; StopRil(); - +#ifdef MOZ_B2G_BT + StopDBus(); +#endif mRILWorker = nsnull; nsCOMPtr wifi(do_QueryInterface(mWifiWorker)); if (wifi) { @@ -363,6 +384,30 @@ SystemWorkerManager::InitWifi(JSContext *cx) return NS_OK; } +nsresult +SystemWorkerManager::InitBluetooth(JSContext *cx) +{ +#ifdef MOZ_B2G_BT +#ifdef MOZ_WIDGET_GONK + // We need a platform specific check here to make sure of when we're + // running on an emulator. Therefore, if we're compiled with gonk, + // see if we can load functions out of bluedroid. If not, assume + // it's an emulator and don't start the bluetooth thread. + if(EnsureBluetoothInit()) { +#endif +#endif + StartDBus(); +#ifdef MOZ_B2G_BT +#ifdef MOZ_WIDGET_GONK + } + else { + LOG("Bluedroid functions not available, assuming running on simulator. Not starting DBus thread."); + } +#endif +#endif + return NS_OK; +} + NS_IMPL_ISUPPORTS2(SystemWorkerManager, nsIObserver, nsIInterfaceRequestor) NS_IMETHODIMP diff --git a/dom/system/gonk/SystemWorkerManager.h b/dom/system/gonk/SystemWorkerManager.h index 9040a226eb9..a2b318cb83e 100644 --- a/dom/system/gonk/SystemWorkerManager.h +++ b/dom/system/gonk/SystemWorkerManager.h @@ -78,7 +78,8 @@ private: nsresult InitRIL(JSContext *cx); nsresult InitWifi(JSContext *cx); - + nsresult InitBluetooth(JSContext *cx); + nsCOMPtr mRILWorker; nsCOMPtr mWifiWorker; diff --git a/ipc/Makefile.in b/ipc/Makefile.in index e334970c61f..98a0d015f6a 100644 --- a/ipc/Makefile.in +++ b/ipc/Makefile.in @@ -47,6 +47,10 @@ ifdef MOZ_B2G_RIL #{ DIRS += ril endif #} +ifdef MOZ_B2G_BT #{ +DIRS += dbus +endif #} + TOOL_DIRS = app include $(topsrcdir)/config/rules.mk diff --git a/ipc/dbus/DBusThread.cpp b/ipc/dbus/DBusThread.cpp new file mode 100644 index 00000000000..a8170588316 --- /dev/null +++ b/ipc/dbus/DBusThread.cpp @@ -0,0 +1,587 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=40: */ +/* + * Copyright 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * NOTE: Due to being based on the dbus compatibility layer for + * android's bluetooth implementation, this file is licensed under the + * apache license instead of MPL. + * + */ + +#include "DBusThread.h" +#include "RawDBusConnection.h" +#include "DBusUtils.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "base/eintr_wrapper.h" +#include "base/message_loop.h" +#include "nsTArray.h" +#include "mozilla/RefPtr.h" +#include "mozilla/Monitor.h" +#include "mozilla/Util.h" +#include "mozilla/FileUtils.h" +#include "nsAutoPtr.h" +#include "nsIThread.h" +#include "nsXULAppAPI.h" + +#undef LOG +#if defined(MOZ_WIDGET_GONK) +#include +#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GonkBluetooth", args); +#else +#define BTDEBUG true +#define LOG(args...) if(BTDEBUG) printf(args); +#endif + +#define DEFAULT_INITIAL_POLLFD_COUNT 8 + +// Functions for converting between unix events in the poll struct, +// and their dbus definitions + +// TODO Add Wakeup to this list once we've moved to ics + +enum { + DBUS_EVENT_LOOP_EXIT = 1, + DBUS_EVENT_LOOP_ADD = 2, + DBUS_EVENT_LOOP_REMOVE = 3, +} DBusEventTypes; + +// Signals that the DBus thread should listen for. Needs to include +// all signals any DBus observer object may need. + +static const char* DBUS_SIGNALS[] = +{ + "type='signal',interface='org.freedesktop.DBus'", + "type='signal',interface='org.bluez.Adapter'", + "type='signal',interface='org.bluez.Device'", + "type='signal',interface='org.bluez.Input'", + "type='signal',interface='org.bluez.Network'", + "type='signal',interface='org.bluez.NetworkServer'", + "type='signal',interface='org.bluez.HealthDevice'", + "type='signal',interface='org.bluez.AudioSink'" +}; + +static unsigned int UnixEventsToDBusFlags(short events) +{ + return (events & DBUS_WATCH_READABLE ? POLLIN : 0) | + (events & DBUS_WATCH_WRITABLE ? POLLOUT : 0) | + (events & DBUS_WATCH_ERROR ? POLLERR : 0) | + (events & DBUS_WATCH_HANGUP ? POLLHUP : 0); +} + +static short DBusFlagsToUnixEvents(unsigned int flags) +{ + return (flags & POLLIN ? DBUS_WATCH_READABLE : 0) | + (flags & POLLOUT ? DBUS_WATCH_WRITABLE : 0) | + (flags & POLLERR ? DBUS_WATCH_ERROR : 0) | + (flags & POLLHUP ? DBUS_WATCH_HANGUP : 0); +} + +namespace mozilla { +namespace ipc { + +struct PollFdComparator { + bool Equals(const pollfd& a, const pollfd& b) const { + return ((a.fd == b.fd) && + (a.events == b.events)); + } + bool LessThan(const pollfd& a, const pollfd&b) const { + return false; + } +}; + +// DBus Thread Class prototype + +struct DBusThread : public RawDBusConnection +{ + DBusThread(); + ~DBusThread(); + + bool StartEventLoop(); + void StopEventLoop(); + bool IsEventLoopRunning(); + static void* EventLoop(void* aPtr); + + // Thread members + pthread_t mThread; + Mutex mMutex; + bool mIsRunning; + + // Information about the sockets we're polling. Socket counts + // increase/decrease depending on how many add/remove watch signals + // we're received via the control sockets. + nsTArray mPollData; + nsTArray mWatchData; + + // Sockets for receiving dbus control information (watch + // add/removes, loop shutdown, etc...) + ScopedClose mControlFdR; + ScopedClose mControlFdW; + +protected: + bool SetUpEventLoop(); + bool TearDownData(); + bool TearDownEventLoop(); +}; + +static nsAutoPtr sDBusThread; + +// DBus utility functions +// Free statics, as they're used as function pointers in dbus setup + +static dbus_bool_t +AddWatch(DBusWatch *aWatch, void *aData) +{ + DBusThread *dbt = (DBusThread *)aData; + + if (dbus_watch_get_enabled(aWatch)) { + // note that we can't just send the watch and inspect it later + // because we may get a removeWatch call before this data is reacted + // to by our eventloop and remove this watch.. reading the add first + // and then inspecting the recently deceased watch would be bad. + char control = DBUS_EVENT_LOOP_ADD; + if(write(dbt->mControlFdW.get(), &control, sizeof(char)) < 0) { + LOG("Cannot write DBus add watch control data to socket!\n"); + return false; + } + + // TODO change this to dbus_watch_get_unix_fd once we move to ics + int fd = dbus_watch_get_fd(aWatch); + if(write(dbt->mControlFdW.get(), &fd, sizeof(int)) < 0) { + LOG("Cannot write DBus add watch descriptor data to socket!\n"); + return false; + } + + unsigned int flags = dbus_watch_get_flags(aWatch); + if(write(dbt->mControlFdW.get(), &flags, sizeof(unsigned int)) < 0) { + LOG("Cannot write DBus add watch flag data to socket!\n"); + return false; + } + + if(write(dbt->mControlFdW.get(), &aWatch, sizeof(DBusWatch*)) < 0) { + LOG("Cannot write DBus add watch struct data to socket!\n"); + return false; + } + } + return true; +} + +static void +RemoveWatch(DBusWatch *aWatch, void *aData) +{ + DBusThread *dbt = (DBusThread *)aData; + + char control = DBUS_EVENT_LOOP_REMOVE; + if(write(dbt->mControlFdW.get(), &control, sizeof(char)) < 0) { + LOG("Cannot write DBus remove watch control data to socket!\n"); + return; + } + + // TODO change this to dbus_watch_get_unix_fd once we move to ics + int fd = dbus_watch_get_fd(aWatch); + if(write(dbt->mControlFdW.get(), &fd, sizeof(int)) < 0) { + LOG("Cannot write DBus remove watch descriptor data to socket!\n"); + return; + } + + unsigned int flags = dbus_watch_get_flags(aWatch); + if(write(dbt->mControlFdW.get(), &flags, sizeof(unsigned int)) < 0) { + LOG("Cannot write DBus remove watch flag data to socket!\n"); + return; + } +} + +static void +ToggleWatch(DBusWatch *aWatch, void *aData) +{ + if (dbus_watch_get_enabled(aWatch)) { + AddWatch(aWatch, aData); + } else { + RemoveWatch(aWatch, aData); + } +} + +static void +HandleWatchAdd(DBusThread* aDbt) +{ + DBusWatch *watch; + int newFD; + unsigned int flags; + if(read(aDbt->mControlFdR.get(), &newFD, sizeof(int)) < 0) { + LOG("Cannot read DBus watch add descriptor data from socket!\n"); + return; + } + if(read(aDbt->mControlFdR.get(), &flags, sizeof(unsigned int)) < 0) { + LOG("Cannot read DBus watch add flag data from socket!\n"); + return; + } + if(read(aDbt->mControlFdR.get(), &watch, sizeof(DBusWatch *)) < 0) { + LOG("Cannot read DBus watch add watch data from socket!\n"); + return; + } + short events = DBusFlagsToUnixEvents(flags); + + pollfd p; + p.fd = newFD; + p.revents = 0; + p.events = events; + if(aDbt->mPollData.Contains(p, PollFdComparator())) return; + aDbt->mPollData.AppendElement(p); + aDbt->mWatchData.AppendElement(watch); +} + +static void HandleWatchRemove(DBusThread* aDbt) { + int removeFD; + unsigned int flags; + + if(read(aDbt->mControlFdR.get(), &removeFD, sizeof(int)) < 0) { + LOG("Cannot read DBus watch remove descriptor data from socket!\n"); + return; + } + if(read(aDbt->mControlFdR.get(), &flags, sizeof(unsigned int)) < 0) { + LOG("Cannot read DBus watch remove flag data from socket!\n"); + return; + } + short events = DBusFlagsToUnixEvents(flags); + pollfd p; + p.fd = removeFD; + p.events = events; + int index = aDbt->mPollData.IndexOf(p, 0, PollFdComparator()); + aDbt->mPollData.RemoveElementAt(index); + + // DBusWatch pointers are maintained by DBus, so we won't leak by + // removing. + aDbt->mWatchData.RemoveElementAt(index); +} + +// Called by dbus during WaitForAndDispatchEventNative() +static DBusHandlerResult +EventFilter(DBusConnection *aConn, DBusMessage *aMsg, + void *aData) +{ + DBusError err; + + dbus_error_init(&err); + + if (dbus_message_get_type(aMsg) != DBUS_MESSAGE_TYPE_SIGNAL) { + LOG("%s: not interested (not a signal).\n", __FUNCTION__); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + LOG("%s: Received signal %s:%s from %s\n", __FUNCTION__, + dbus_message_get_interface(aMsg), dbus_message_get_member(aMsg), + dbus_message_get_path(aMsg)); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +// DBus Thread Implementation + +DBusThread::DBusThread() : mMutex("DBusGonk.mMutex") + , mIsRunning(false) +{ +} + +DBusThread::~DBusThread() +{ + +} + +bool +DBusThread::SetUpEventLoop() +{ + // If we already have a connection, exit + if(mConnection) { + return false; + } + + dbus_threads_init_default(); + DBusError err; + dbus_error_init(&err); + + // If we can't establish a connection to dbus, nothing else will work + if(!Create()) { + return false; + } + + // Add a filter for all incoming messages_base + if (!dbus_connection_add_filter(mConnection, EventFilter, this, NULL)){ + return false; + } + + // Set which messages will be processed by this dbus connection. + // Since we are maintaining a single thread for all the DBus bluez + // signals we want, register all of them in this thread at startup. + // The event handler will sort the destinations out as needed. + for(uint32_t i = 0; i < ArrayLength(DBUS_SIGNALS); ++i) { + dbus_bus_add_match(mConnection, + DBUS_SIGNALS[i], + &err); + if (dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR(&err); + return false; + } + } + return true; +} + +bool +DBusThread::TearDownData() +{ + LOG("Removing DBus Bluetooth Sockets\n"); + if (mControlFdW.get()) { + mControlFdW.dispose(); + } + if (mControlFdR.get()) { + mControlFdR.dispose(); + } + mPollData.Clear(); + + // DBusWatch pointers are maintained by DBus, so we won't leak by + // clearing. + mWatchData.Clear(); + return true; +} + +bool +DBusThread::TearDownEventLoop() +{ + MOZ_ASSERT(mConnection); + + DBusError err; + dbus_error_init(&err); + + for(uint32_t i = 0; i < ArrayLength(DBUS_SIGNALS); ++i) { + dbus_bus_remove_match(mConnection, + DBUS_SIGNALS[i], + &err); + if (dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR(&err); + } + } + + dbus_connection_remove_filter(mConnection, EventFilter, this); + return true; +} + +void* +DBusThread::EventLoop(void *aPtr) +{ + DBusThread* dbt = static_cast(aPtr); + MOZ_ASSERT(dbt); + + dbus_connection_set_watch_functions(dbt->mConnection, AddWatch, + RemoveWatch, ToggleWatch, aPtr, NULL); + + dbt->mIsRunning = true; + LOG("DBus Bluetooth Event Loop Starting\n"); + while (1) { + poll(dbt->mPollData.Elements(), dbt->mPollData.Length(), -1); + + for (uint32_t i = 0; i < dbt->mPollData.Length(); i++) { + if (!dbt->mPollData[i].revents) { + continue; + } + + if (dbt->mPollData[i].fd == dbt->mControlFdR.get()) { + char data; + while (recv(dbt->mControlFdR.get(), &data, sizeof(char), MSG_DONTWAIT) + != -1) { + switch (data) { + case DBUS_EVENT_LOOP_EXIT: + { + LOG("DBus Bluetooth Event Loop Exiting\n"); + dbus_connection_set_watch_functions(dbt->mConnection, + NULL, NULL, NULL, NULL, NULL); + dbt->TearDownEventLoop(); + return NULL; + } + case DBUS_EVENT_LOOP_ADD: + { + HandleWatchAdd(dbt); + break; + } + case DBUS_EVENT_LOOP_REMOVE: + { + HandleWatchRemove(dbt); + break; + } + } + } + } else { + short events = dbt->mPollData[i].revents; + unsigned int flags = UnixEventsToDBusFlags(events); + dbus_watch_handle(dbt->mWatchData[i], flags); + dbt->mPollData[i].revents = 0; + // Break at this point since we don't know if the operation + // was destructive + break; + } + } + while (dbus_connection_dispatch(dbt->mConnection) == + DBUS_DISPATCH_DATA_REMAINS) + {} + } +} + +bool +DBusThread::StartEventLoop() +{ + MutexAutoLock lock(mMutex); + mIsRunning = false; + + // socketpair opens two sockets for the process to communicate on. + // This is how android's implementation of the dbus event loop + // communicates with itself in relation to IPC signals. These + // sockets are contained sequentially in the same struct in the + // android code, but we break them out into class members here. + // Therefore we read into a local array and then copy. + + int sockets[2]; + if (socketpair(AF_LOCAL, SOCK_STREAM, 0, (int*)(&sockets)) < 0) { + TearDownData(); + return false; + } + mControlFdR.rwget() = sockets[0]; + mControlFdW.rwget() = sockets[1]; + pollfd p; + p.fd = mControlFdR.get(); + p.events = POLLIN; + mPollData.AppendElement(p); + + // Due to the fact that mPollData and mWatchData have to match, we + // push a null to the front of mWatchData since it has the control + // fd in the first slot of mPollData. + + mWatchData.AppendElement((DBusWatch*)NULL); + if (!SetUpEventLoop()) { + TearDownData(); + return false; + } + LOG("DBus Bluetooth Thread Starting\n"); + pthread_create(&(mThread), NULL, DBusThread::EventLoop, this); + return true; +} + +void +DBusThread::StopEventLoop() +{ + MutexAutoLock lock(mMutex); + if (mIsRunning) { + char data = DBUS_EVENT_LOOP_EXIT; + ssize_t wret = write(mControlFdW.get(), &data, sizeof(char)); + if(wret < 0) { + LOG("Cannot write exit bit to DBus Bluetooth Thread!\n"); + } + void *ret; + LOG("DBus Bluetooth Thread Joining\n"); + pthread_join(mThread, &ret); + LOG("DBus Bluetooth Thread Joined\n"); + TearDownData(); + } + mIsRunning = false; +} + +bool +DBusThread::IsEventLoopRunning() +{ + MutexAutoLock lock(mMutex); + return mIsRunning; +} + +// Startup/Shutdown utility functions + +static void +ConnectDBus(Monitor* aMonitor, bool* aSuccess) +{ + MOZ_ASSERT(!sDBusThread); + + sDBusThread = new DBusThread(); + *aSuccess = true; + if(!sDBusThread->StartEventLoop()) + { + *aSuccess = false; + } + { + MonitorAutoLock lock(*aMonitor); + lock.Notify(); + } +} + +static void +DisconnectDBus(Monitor* aMonitor, bool* aSuccess) +{ + MOZ_ASSERT(sDBusThread); + + *aSuccess = true; + sDBusThread->StopEventLoop(); + sDBusThread = NULL; + { + MonitorAutoLock lock(*aMonitor); + lock.Notify(); + } +} + +bool +StartDBus() +{ + Monitor monitor("StartDBus.monitor"); + bool success; + { + MonitorAutoLock lock(monitor); + + XRE_GetIOMessageLoop()->PostTask( + FROM_HERE, + NewRunnableFunction(ConnectDBus, &monitor, &success)); + lock.Wait(); + } + return success; +} + +bool +StopDBus() +{ + Monitor monitor("StopDBus.monitor"); + bool success; + { + MonitorAutoLock lock(monitor); + + XRE_GetIOMessageLoop()->PostTask( + FROM_HERE, + NewRunnableFunction(DisconnectDBus, &monitor, &success)); + lock.Wait(); + } + return success; +} + +} +} diff --git a/ipc/dbus/DBusThread.h b/ipc/dbus/DBusThread.h new file mode 100644 index 00000000000..7e0942c0bd1 --- /dev/null +++ b/ipc/dbus/DBusThread.h @@ -0,0 +1,25 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=40: */ +/* 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/. */ + +#ifndef mozilla_ipc_dbus_gonk_dbusthread_h__ +#define mozilla_ipc_dbus_gonk_dbusthread_h__ + +namespace mozilla { +namespace ipc { + + +// Starts the DBus thread, which handles returning signals to objects +// that call asynchronous functions. This should be called from the +// main thread at startup. +bool StartDBus(); + +// Stop the DBus thread, assuming it's currently running. Should be +// called from main thread. +bool StopDBus(); +} +} +#endif + diff --git a/ipc/dbus/DBusUtils.cpp b/ipc/dbus/DBusUtils.cpp new file mode 100644 index 00000000000..3d1d6972bb1 --- /dev/null +++ b/ipc/dbus/DBusUtils.cpp @@ -0,0 +1,47 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=40: */ +/* +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include +#include "dbus/dbus.h" + +#undef LOG +#if defined(MOZ_WIDGET_GONK) +#include +#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gonk", args); +#else +#define LOG(args...) printf(args); +#endif + +namespace mozilla { +namespace ipc { + +void +log_and_free_dbus_error(DBusError* err, const char* function, DBusMessage* msg) +{ + if(msg) { + LOG("%s: D-Bus error in %s: %s (%s)", function, + dbus_message_get_member((msg)), (err)->name, (err)->message); + } else { + LOG("%s: D-Bus error: %s (%s)", __FUNCTION__, + (err)->name, (err)->message); + } + dbus_error_free((err)); +} + +} +} diff --git a/ipc/dbus/DBusUtils.h b/ipc/dbus/DBusUtils.h new file mode 100644 index 00000000000..c7d90ade578 --- /dev/null +++ b/ipc/dbus/DBusUtils.h @@ -0,0 +1,37 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=40: */ +/* +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef mozilla_ipc_dbus_dbusutils_h__ +#define mozilla_ipc_dbus_dbusutils_h__ + +// LOGE and free a D-Bus error +// Using #define so that __FUNCTION__ resolves usefully +#define LOG_AND_FREE_DBUS_ERROR_WITH_MSG(err, msg) log_and_free_dbus_error(err, __FUNCTION__, msg); +#define LOG_AND_FREE_DBUS_ERROR(err) log_and_free_dbus_error(err, __FUNCTION__); + +struct DBusMessage; +struct DBusError; + +namespace mozilla { +namespace ipc { +void log_and_free_dbus_error(DBusError* err, const char* function, DBusMessage* msg = NULL); +} +} + +#endif + diff --git a/ipc/dbus/Makefile.in b/ipc/dbus/Makefile.in new file mode 100644 index 00000000000..6005e24636a --- /dev/null +++ b/ipc/dbus/Makefile.in @@ -0,0 +1,44 @@ +# 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/. + +DEPTH = ../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +MODULE = ipc +LIBRARY_NAME = mozdbus_s +FORCE_STATIC_LIB = 1 +LIBXUL_LIBRARY = 1 +EXPORT_LIBRARY = 1 + +ifdef MOZ_ENABLE_DBUS + LOCAL_INCLUDES += $(MOZ_DBUS_CFLAGS) +endif + +EXPORTS_NAMESPACES = mozilla/ipc + +EXPORTS_mozilla/ipc = \ + RawDBusConnection.h \ + DBusThread.h \ + DBusUtils.h \ + $(NULL) + +CPPSRCS += \ + RawDBusConnection.cpp \ + DBusThread.cpp \ + DBusUtils.cpp \ + $(NULL) + +include $(topsrcdir)/config/config.mk +include $(topsrcdir)/ipc/chromium/chromium-config.mk + +include $(topsrcdir)/config/rules.mk + +ifdef MOZ_ENABLE_DBUS + CFLAGS += $(MOZ_DBUS_GLIB_CFLAGS) + CXXFLAGS += $(MOZ_DBUS_GLIB_CFLAGS) +endif diff --git a/ipc/dbus/RawDBusConnection.cpp b/ipc/dbus/RawDBusConnection.cpp new file mode 100644 index 00000000000..6b92ae3c729 --- /dev/null +++ b/ipc/dbus/RawDBusConnection.cpp @@ -0,0 +1,29 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=40: */ +/* 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 "RawDBusConnection.h" +#include + +using namespace mozilla::ipc; + +RawDBusConnection::RawDBusConnection() { +} + +RawDBusConnection::~RawDBusConnection() { +} + +bool RawDBusConnection::Create() { + DBusError err; + dbus_error_init(&err); + mConnection = dbus_bus_get(DBUS_BUS_SYSTEM, &err); + if (dbus_error_is_set(&err)) { + dbus_error_free(&err); + return false; + } + dbus_connection_set_exit_on_disconnect(mConnection, FALSE); + return true; +} + diff --git a/ipc/dbus/RawDBusConnection.h b/ipc/dbus/RawDBusConnection.h new file mode 100644 index 00000000000..6ae6498177a --- /dev/null +++ b/ipc/dbus/RawDBusConnection.h @@ -0,0 +1,41 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=40: */ +/* 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/. */ + +#ifndef mozilla_ipc_dbus_gonk_rawdbusconnection_h__ +#define mozilla_ipc_dbus_gonk_rawdbusconnection_h__ + +#include +#include +#include +#include +#include +#include "mozilla/Scoped.h" +#include "dbus/dbus.h" + +struct DBusConnection; + +namespace mozilla { +namespace ipc { + +class RawDBusConnection +{ + struct ScopedDBusConnectionPtrTraits : ScopedFreePtrTraits + { + static void release(DBusConnection* ptr) { if(ptr) dbus_connection_unref(ptr); } + }; + +public: + RawDBusConnection(); + ~RawDBusConnection(); + bool Create(); +protected: + Scoped mConnection; +}; + +} +} + +#endif diff --git a/toolkit/library/Makefile.in b/toolkit/library/Makefile.in index f40fc4d4077..e89a670186a 100644 --- a/toolkit/library/Makefile.in +++ b/toolkit/library/Makefile.in @@ -113,6 +113,10 @@ ifdef MOZ_B2G_RIL #{ STATIC_LIBS += mozril_s endif #} +ifdef MOZ_B2G_BT #{ +STATIC_LIBS += mozdbus_s +endif #} + ifdef MOZ_IPDL_TESTS STATIC_LIBS += ipdlunittest_s endif @@ -398,6 +402,7 @@ OS_LIBS += \ -lcamera_client \ -lbinder \ -lsensorservice \ + -ldbus \ $(NULL) endif