Bug 744349 - Create message distribution mechanism for DBus Bluetooth Signals; r=cjones

This commit is contained in:
Kyle Machulis 2012-06-02 11:23:16 -07:00
parent 2cbd830416
commit 8b8e15f423
15 changed files with 848 additions and 79 deletions

View File

@ -5,7 +5,7 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "BluetoothAdapter.h"
#include "BluetoothFirmware.h"
#include "BluetoothUtils.h"
#include "nsDOMClassInfo.h"
#include "nsDOMEvent.h"
@ -36,3 +36,29 @@ NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
NS_IMPL_ADDREF_INHERITED(BluetoothAdapter, nsDOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(BluetoothAdapter, nsDOMEventTargetHelper)
BluetoothAdapter::BluetoothAdapter(const nsCString& name) :
mName(name)
{
}
BluetoothAdapter::~BluetoothAdapter()
{
if (NS_FAILED(UnregisterBluetoothEventHandler(mName, this))) {
NS_WARNING("Failed to unregister object with observer!");
}
}
// static
already_AddRefed<BluetoothAdapter>
BluetoothAdapter::Create(const nsCString& name) {
nsRefPtr<BluetoothAdapter> adapter = new BluetoothAdapter(name);
if (NS_FAILED(RegisterBluetoothEventHandler(name, adapter))) {
NS_WARNING("Failed to register object with observer!");
return NULL;
}
return adapter.forget();
}
void BluetoothAdapter::Notify(const BluetoothEvent& aData) {
printf("Got an adapter message!\n");
}

View File

@ -11,6 +11,7 @@
#include "nsDOMEventTargetHelper.h"
#include "nsIDOMBluetoothAdapter.h"
#include "nsIDOMDOMRequest.h"
#include "mozilla/Observer.h"
class nsIEventTarget;
@ -18,6 +19,7 @@ BEGIN_BLUETOOTH_NAMESPACE
class BluetoothAdapter : public nsDOMEventTargetHelper
, public nsIDOMBluetoothAdapter
, public BluetoothEventObserver
{
public:
NS_DECL_ISUPPORTS_INHERITED
@ -28,6 +30,16 @@ public:
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(BluetoothAdapter,
nsDOMEventTargetHelper)
static already_AddRefed<BluetoothAdapter>
Create(const nsCString& name);
void Notify(const BluetoothEvent& aParam);
protected:
nsCString mName;
private:
BluetoothAdapter() {}
BluetoothAdapter(const nsCString& name);
~BluetoothAdapter();
};
END_BLUETOOTH_NAMESPACE

View File

@ -7,6 +7,10 @@
#ifndef mozilla_dom_bluetooth_bluetoothcommon_h__
#define mozilla_dom_bluetooth_bluetoothcommon_h__
#include "nsString.h"
#include "nsTArray.h"
#include "mozilla/Observer.h"
#define BEGIN_BLUETOOTH_NAMESPACE \
namespace mozilla { namespace dom { namespace bluetooth {
#define END_BLUETOOTH_NAMESPACE \
@ -14,6 +18,48 @@
#define USING_BLUETOOTH_NAMESPACE \
using namespace mozilla::dom::bluetooth;
class nsIDOMBluetooth;
class nsCString;
BEGIN_BLUETOOTH_NAMESPACE
/**
* BluetoothEvents usually hand back one of 3 types:
*
* - 32-bit Int
* - String
* - Bool
*
* BluetoothVariant encases the types into a single structure.
*/
struct BluetoothVariant
{
uint32_t mUint32;
nsCString mString;
};
/**
* BluetoothNamedVariant is a variant with a name value, for passing around
* things like properties with variant values.
*/
struct BluetoothNamedVariant
{
nsCString mName;
BluetoothVariant mValue;
};
/**
* BluetoothEvent holds a variant value and the name of an event, such as
* PropertyChanged or DeviceFound.
*/
struct BluetoothEvent
{
nsCString mEventName;
nsTArray<BluetoothNamedVariant> mValues;
};
typedef mozilla::Observer<BluetoothEvent> BluetoothEventObserver;
typedef mozilla::ObserverList<BluetoothEvent> BluetoothEventObserverList;
END_BLUETOOTH_NAMESPACE
#endif // mozilla_dom_bluetooth_bluetoothcommon_h__

View File

@ -8,6 +8,7 @@
#include "BluetoothCommon.h"
#include "BluetoothFirmware.h"
#include "BluetoothAdapter.h"
#include "BluetoothUtils.h"
#include "nsIDocument.h"
#include "nsIURI.h"
@ -30,6 +31,7 @@
#define DOM_BLUETOOTH_URL_PREF "dom.mozBluetooth.whitelist"
using namespace mozilla;
using mozilla::Preferences;
USING_BLUETOOTH_NAMESPACE
@ -37,17 +39,19 @@ USING_BLUETOOTH_NAMESPACE
static void
FireEnabled(bool aResult, nsIDOMDOMRequest* aDomRequest)
{
nsCOMPtr<nsIDOMRequestService> rs = do_GetService("@mozilla.org/dom/dom-request-service;1");
nsCOMPtr<nsIDOMRequestService> rs =
do_GetService("@mozilla.org/dom/dom-request-service;1");
if (!rs) {
NS_WARNING("No DOMRequest Service!");
return;
}
mozilla::DebugOnly<nsresult> rv = aResult ?
rs->FireSuccess(aDomRequest, JSVAL_VOID) :
rs->FireError(aDomRequest,
NS_LITERAL_STRING("Bluetooth firmware loading failed"));
DebugOnly<nsresult> rv =
aResult ?
rs->FireSuccess(aDomRequest, JSVAL_VOID) :
rs->FireError(aDomRequest,
NS_LITERAL_STRING("Bluetooth firmware loading failed"));
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Bluetooth firmware loading failed");
}
@ -120,7 +124,8 @@ class ToggleBtResultTask : public nsRunnable
class ToggleBtTask : public nsRunnable
{
public:
ToggleBtTask(bool aEnabled, nsIDOMDOMRequest* aReq, BluetoothManager* aManager)
ToggleBtTask(bool aEnabled, nsIDOMDOMRequest* aReq,
BluetoothManager* aManager)
: mEnabled(aEnabled),
mManagerPtr(aManager),
mDOMRequest(aReq)
@ -177,11 +182,19 @@ class ToggleBtTask : public nsRunnable
};
BluetoothManager::BluetoothManager(nsPIDOMWindow *aWindow) :
mEnabled(false)
mEnabled(false),
mName(nsDependentCString("/"))
{
BindToOwner(aWindow);
}
BluetoothManager::~BluetoothManager()
{
if(NS_FAILED(UnregisterBluetoothEventHandler(mName, this))) {
NS_WARNING("Failed to unregister object with observer!");
}
}
NS_IMETHODIMP
BluetoothManager::SetEnabled(bool aEnabled, nsIDOMDOMRequest** aDomRequest)
{
@ -220,12 +233,32 @@ BluetoothManager::GetEnabled(bool* aEnabled)
NS_IMETHODIMP
BluetoothManager::GetDefaultAdapter(nsIDOMBluetoothAdapter** aAdapter)
{
//TODO: Implement adapter fetching
return NS_ERROR_FAILURE;
nsCString path;
nsresult rv = GetDefaultAdapterPathInternal(path);
if(NS_FAILED(rv)) {
NS_WARNING("Cannot fetch adapter path!");
return NS_ERROR_FAILURE;
}
nsRefPtr<BluetoothAdapter> adapter = BluetoothAdapter::Create(path);
adapter.forget(aAdapter);
return NS_OK;
}
// static
already_AddRefed<BluetoothManager>
BluetoothManager::Create(nsPIDOMWindow* aWindow) {
nsRefPtr<BluetoothManager> manager = new BluetoothManager(aWindow);
nsDependentCString name("/");
if(NS_FAILED(RegisterBluetoothEventHandler(name, manager))) {
NS_WARNING("Failed to register object with observer!");
return NULL;
}
return manager.forget();
}
nsresult
NS_NewBluetoothManager(nsPIDOMWindow* aWindow, nsIDOMBluetoothManager** aBluetoothManager)
NS_NewBluetoothManager(nsPIDOMWindow* aWindow,
nsIDOMBluetoothManager** aBluetoothManager)
{
NS_ASSERTION(aWindow, "Null pointer!");
@ -238,8 +271,12 @@ NS_NewBluetoothManager(nsPIDOMWindow* aWindow, nsIDOMBluetoothManager** aBluetoo
return NS_OK;
}
nsRefPtr<BluetoothManager> bluetoothManager = new BluetoothManager(aWindow);
nsRefPtr<BluetoothManager> bluetoothManager = BluetoothManager::Create(aWindow);
bluetoothManager.forget(aBluetoothManager);
return NS_OK;
}
void BluetoothManager::Notify(const BluetoothEvent& aData) {
printf("Received an manager message!\n");
}

View File

@ -10,13 +10,14 @@
#include "BluetoothCommon.h"
#include "nsDOMEventTargetHelper.h"
#include "nsIDOMBluetoothManager.h"
#include "nsWeakReference.h"
#include "mozilla/Observer.h"
BEGIN_BLUETOOTH_NAMESPACE
class BluetoothAdapter;
class BluetoothManager : public nsDOMEventTargetHelper
, public nsIDOMBluetoothManager
, public nsIDOMBluetoothManager
, public BluetoothEventObserver
{
public:
NS_DECL_ISUPPORTS_INHERITED
@ -27,11 +28,18 @@ public:
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(BluetoothManager,
nsDOMEventTargetHelper)
BluetoothManager(nsPIDOMWindow*);
inline void SetEnabledInternal(bool aEnabled) {mEnabled = aEnabled;}
static already_AddRefed<BluetoothManager>
Create(nsPIDOMWindow* aWindow);
void Notify(const BluetoothEvent& aData);
private:
BluetoothManager() {}
BluetoothManager(nsPIDOMWindow* aWindow);
~BluetoothManager();
bool mEnabled;
nsCString mName;
NS_DECL_EVENT_HANDLER(enabled)
@ -40,6 +48,7 @@ private:
END_BLUETOOTH_NAMESPACE
nsresult NS_NewBluetoothManager(nsPIDOMWindow* aWindow, nsIDOMBluetoothManager** aBluetoothManager);
nsresult NS_NewBluetoothManager(nsPIDOMWindow* aWindow,
nsIDOMBluetoothManager** aBluetoothManager);
#endif

View File

@ -0,0 +1,77 @@
/* -*- 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_bluetoothutils_h__
#define mozilla_dom_bluetooth_bluetoothutils_h__
#include "BluetoothCommon.h"
BEGIN_BLUETOOTH_NAMESPACE
/**
* BluetoothUtil functions are used to dispatch messages to Bluetooth DOM
* objects on the main thread, as well as provide platform indenpendent access
* to BT functionality. Tasks for polling for outside messages will usually
* happen on the IO Thread (see ipc/dbus for instance), and these messages will
* be encased in runnables that will then be distributed via observers managed
* here.
*/
/**
* Add a message handler object from message distribution observer.
* Must be called from the main thread.
*
* @param aNodeName Node name of the object
* @param aMsgHandler Weak pointer to the object
*
* @return NS_OK on successful addition to observer, NS_ERROR_FAILED
* otherwise
*/
nsresult RegisterBluetoothEventHandler(const nsCString& aNodeName,
BluetoothEventObserver *aMsgHandler);
/**
* Remove a message handler object from message distribution observer.
* Must be called from the main thread.
*
* @param aNodeName Node name of the object
* @param aMsgHandler Weak pointer to the object
*
* @return NS_OK on successful removal from observer service,
* NS_ERROR_FAILED otherwise
*/
nsresult UnregisterBluetoothEventHandler(const nsCString& aNodeName,
BluetoothEventObserver *aMsgHandler);
/**
* Returns the path of the default adapter, implemented via a platform
* specific method.
*
* @return Default adapter path/name on success, NULL otherwise
*/
nsresult GetDefaultAdapterPathInternal(nsCString& aAdapterPath);
/**
* Set up variables and start the platform specific connection. Must
* be called from main thread.
*
* @return NS_OK if connection starts successfully, NS_ERROR_FAILURE
* otherwise
*/
nsresult StartBluetoothConnection();
/**
* Stop the platform specific connection. Must be called from main
* thread.
*
* @return NS_OK if connection starts successfully, NS_ERROR_FAILURE
* otherwise
*/
nsresult StopBluetoothConnection();
END_BLUETOOTH_NAMESPACE
#endif

View File

@ -15,6 +15,16 @@ XPIDL_MODULE = dom_bluetooth
LIBXUL_LIBRARY = 1
FORCE_STATIC_LIB = 1
ifeq (gonk,$(MOZ_WIDGET_TOOLKIT))
VPATH += $(srcdir)/linux
LOCAL_INCLUDES += $(MOZ_DBUS_CFLAGS)
endif
ifdef MOZ_ENABLE_DBUS
VPATH += $(srcdir)/linux
LOCAL_INCLUDES += $(MOZ_DBUS_CFLAGS)
endif
include $(topsrcdir)/dom/dom-config.mk
CPPSRCS = \
@ -23,23 +33,23 @@ CPPSRCS = \
BluetoothFirmware.cpp \
$(NULL)
ifdef MOZ_ENABLE_DBUS
CPPSRCS += BluetoothDBusUtils.cpp
endif
ifeq (gonk,$(MOZ_WIDGET_TOOLKIT))
CPPSRCS += BluetoothDBusUtils.cpp
endif
XPIDLSRCS = \
nsIDOMNavigatorBluetooth.idl \
nsIDOMBluetoothManager.idl \
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

View File

@ -0,0 +1,270 @@
/* -*- 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 "BluetoothUtils.h"
#include <cstdio>
#include <dbus/dbus.h>
#include "nsAutoPtr.h"
#include "nsThreadUtils.h"
#include "nsDebug.h"
#include "nsClassHashtable.h"
#include "mozilla/ipc/DBusUtils.h"
#include "mozilla/ipc/RawDBusConnection.h"
using namespace mozilla::ipc;
namespace mozilla {
namespace dom {
namespace bluetooth {
static nsAutoPtr<RawDBusConnection> sDBusConnection;
#undef LOG
#if defined(MOZ_WIDGET_GONK)
#include <android/log.h>
#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GonkDBus", args);
#else
#define BTDEBUG true
#define LOG(args...) if (BTDEBUG) printf(args);
#endif
#define DBUS_ADAPTER_IFACE BLUEZ_DBUS_BASE_IFC ".Adapter"
#define DBUS_DEVICE_IFACE BLUEZ_DBUS_BASE_IFC ".Device"
#define BLUEZ_DBUS_BASE_PATH "/org/bluez"
#define BLUEZ_DBUS_BASE_IFC "org.bluez"
#define BLUEZ_ERROR_IFC "org.bluez.Error"
static const char* BLUETOOTH_DBUS_SIGNALS[] =
{
"type='signal',interface='org.freedesktop.DBus'",
"type='signal',interface='org.bluez.Adapter'",
"type='signal',interface='org.bluez.Manager'",
"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'"
};
typedef nsClassHashtable<nsCStringHashKey, BluetoothEventObserverList >
BluetoothEventObserverTable;
static nsAutoPtr<BluetoothEventObserverTable> sBluetoothEventObserverTable;
nsresult
RegisterBluetoothEventHandler(const nsCString& aNodeName,
BluetoothEventObserver* aHandler)
{
MOZ_ASSERT(NS_IsMainThread());
BluetoothEventObserverList *ol;
if (!sBluetoothEventObserverTable->Get(aNodeName, &ol)) {
sBluetoothEventObserverTable->Put(aNodeName,
new BluetoothEventObserverList());
}
sBluetoothEventObserverTable->Get(aNodeName, &ol);
ol->AddObserver(aHandler);
return NS_OK;
}
nsresult
UnregisterBluetoothEventHandler(const nsCString& aNodeName,
BluetoothEventObserver* aHandler)
{
MOZ_ASSERT(NS_IsMainThread());
BluetoothEventObserverList *ol;
if (!sBluetoothEventObserverTable->Get(aNodeName, &ol)) {
NS_WARNING("Node does not exist to remove BluetoothEventListener from!");
return NS_ERROR_FAILURE;
}
sBluetoothEventObserverTable->Get(aNodeName, &ol);
ol->RemoveObserver(aHandler);
if (ol->Length() == 0) {
sBluetoothEventObserverTable->Remove(aNodeName);
}
return NS_OK;
}
struct DistributeDBusMessageTask : public nsRunnable {
DistributeDBusMessageTask(DBusMessage* aMsg) : mMsg(aMsg)
{
}
NS_IMETHOD Run()
{
if (dbus_message_get_path(mMsg.get()) == NULL) {
return NS_OK;
}
MOZ_ASSERT(NS_IsMainThread());
// Notify observers that a message has been sent
nsDependentCString path(dbus_message_get_path(mMsg.get()));
nsDependentCString member(dbus_message_get_member(mMsg.get()));
BluetoothEventObserverList *ol;
if (!sBluetoothEventObserverTable->Get(path, &ol)) {
LOG("No objects registered for %s, returning\n",
dbus_message_get_path(mMsg.get()));
return NS_OK;
}
BluetoothEvent e;
e.mEventName = member;
ol->Broadcast(e);
return NS_OK;
}
DBusMessageRefPtr mMsg;
};
// Called by dbus during WaitForAndDispatchEventNative()
// This function is called on the IOThread
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));
// TODO: Parse DBusMessage* on the IOThread and return as a BluetoothEvent so
// we aren't passing the pointer at all, as well as offloading parsing (not
// that it's that heavy.)
nsCOMPtr<DistributeDBusMessageTask> t(new DistributeDBusMessageTask(aMsg));
if (NS_FAILED(NS_DispatchToMainThread(t))) {
NS_WARNING("Failed to dispatch to main thread!");
}
return DBUS_HANDLER_RESULT_HANDLED;
}
nsresult
StartBluetoothConnection()
{
if(sDBusConnection) {
NS_WARNING("DBusConnection already established, skipping");
return NS_OK;
}
sBluetoothEventObserverTable = new BluetoothEventObserverTable();
sBluetoothEventObserverTable->Init(100);
sDBusConnection = new RawDBusConnection();
sDBusConnection->EstablishDBusConnection();
// Add a filter for all incoming messages_base
if (!dbus_connection_add_filter(sDBusConnection->mConnection, EventFilter,
NULL, NULL)) {
NS_WARNING("Cannot create DBus Event Filter for DBus Thread!");
return NS_ERROR_FAILURE;
}
return NS_OK;
}
nsresult
StopBluetoothConnection()
{
if(!sDBusConnection) {
NS_WARNING("DBusConnection does not exist, nothing to stop, skipping.");
return NS_OK;
}
dbus_connection_remove_filter(sDBusConnection->mConnection, EventFilter, NULL);
sDBusConnection = NULL;
sBluetoothEventObserverTable->Clear();
sBluetoothEventObserverTable = NULL;
return NS_OK;
}
nsresult
GetDefaultAdapterPathInternal(nsCString& aAdapterPath)
{
DBusMessage *msg = NULL, *reply = NULL;
DBusError err;
const char *device_path = NULL;
int attempt = 0;
for (attempt = 0; attempt < 1000 && reply == NULL; attempt ++) {
msg = dbus_message_new_method_call("org.bluez", "/",
"org.bluez.Manager", "DefaultAdapter");
if (!msg) {
LOG("%s: Can't allocate new method call for get_adapter_path!",
__FUNCTION__);
return NS_ERROR_FAILURE;
}
dbus_message_append_args(msg, DBUS_TYPE_INVALID);
dbus_error_init(&err);
reply = dbus_connection_send_with_reply_and_block(
sDBusConnection->mConnection, msg, -1, &err);
if (!reply) {
if (dbus_error_is_set(&err)) {
if (dbus_error_has_name(&err,
"org.freedesktop.DBus.Error.ServiceUnknown")) {
// bluetoothd is still down, retry
LOG("Service unknown\n");
dbus_error_free(&err);
//usleep(10000); // 10 ms
continue;
} else if (dbus_error_has_name(&err,
"org.bluez.Error.NoSuchAdapter")) {
LOG("No adapter found\n");
dbus_error_free(&err);
goto failed;
} else {
// Some other error we weren't expecting
LOG("other error\n");
dbus_error_free(&err);
}
}
}
}
if (attempt == 1000) {
LOG("timeout\n");
//printfE("Time out while trying to get Adapter path, is bluetoothd up ?");
goto failed;
}
if (!dbus_message_get_args(reply, &err, DBUS_TYPE_OBJECT_PATH,
&device_path, DBUS_TYPE_INVALID)
|| !device_path) {
if (dbus_error_is_set(&err)) {
dbus_error_free(&err);
}
goto failed;
}
dbus_message_unref(msg);
aAdapterPath = nsDependentCString(device_path);
return NS_OK;
failed:
dbus_message_unref(msg);
return NS_ERROR_FAILURE;
}
}
}
}

View File

@ -22,6 +22,7 @@
#ifdef MOZ_B2G_BT
#include "mozilla/ipc/DBusThread.h"
#include "BluetoothFirmware.h"
#include "BluetoothUtils.h"
#endif
#include "nsContentUtils.h"
#include "nsServiceManagerUtils.h"
@ -234,6 +235,7 @@ SystemWorkerManager::Init()
NS_WARNING("Failed to initialize Bluetooth!");
return rv;
}
#endif
#ifdef MOZ_WIDGET_GONK
@ -395,6 +397,7 @@ SystemWorkerManager::InitBluetooth(JSContext *cx)
if(EnsureBluetoothInit()) {
#endif
StartDBus();
StartBluetoothConnection();
#ifdef MOZ_WIDGET_GONK
}
else {

View File

@ -45,18 +45,21 @@
#include "base/eintr_wrapper.h"
#include "base/message_loop.h"
#include "nsTArray.h"
#include "nsDataHashtable.h"
#include "mozilla/RefPtr.h"
#include "mozilla/Monitor.h"
#include "mozilla/Util.h"
#include "mozilla/FileUtils.h"
#include "nsAutoPtr.h"
#include "nsThreadUtils.h"
#include "nsIThread.h"
#include "nsXULAppAPI.h"
#include "nsServiceManagerUtils.h"
#include "nsCOMPtr.h"
#undef LOG
#if defined(MOZ_WIDGET_GONK)
#include <android/log.h>
#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GonkBluetooth", args);
#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GonkDBus", args);
#else
#define BTDEBUG true
#define LOG(args...) if(BTDEBUG) printf(args);
@ -82,6 +85,7 @@ static const char* DBUS_SIGNALS[] =
{
"type='signal',interface='org.freedesktop.DBus'",
"type='signal',interface='org.bluez.Adapter'",
"type='signal',interface='org.bluez.Manager'",
"type='signal',interface='org.bluez.Device'",
"type='signal',interface='org.bluez.Input'",
"type='signal',interface='org.bluez.Network'",
@ -291,27 +295,6 @@ static void HandleWatchRemove(DBusThread* aDbt) {
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")
@ -337,12 +320,9 @@ DBusThread::SetUpEventLoop()
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)){
nsresult rv = EstablishDBusConnection();
if(NS_FAILED(rv)) {
NS_WARNING("Cannot create DBus Connection for DBus Thread!");
return false;
}
@ -365,7 +345,7 @@ DBusThread::SetUpEventLoop()
bool
DBusThread::TearDownData()
{
LOG("Removing DBus Bluetooth Sockets\n");
LOG("Removing DBus Sockets\n");
if (mControlFdW.get()) {
mControlFdW.dispose();
}
@ -397,7 +377,6 @@ DBusThread::TearDownEventLoop()
}
}
dbus_connection_remove_filter(mConnection, EventFilter, this);
return true;
}
@ -411,7 +390,7 @@ DBusThread::EventLoop(void *aPtr)
RemoveWatch, ToggleWatch, aPtr, NULL);
dbt->mIsRunning = true;
LOG("DBus Bluetooth Event Loop Starting\n");
LOG("DBus Event Loop Starting\n");
while (1) {
poll(dbt->mPollData.Elements(), dbt->mPollData.Length(), -1);
@ -427,7 +406,7 @@ DBusThread::EventLoop(void *aPtr)
switch (data) {
case DBUS_EVENT_LOOP_EXIT:
{
LOG("DBus Bluetooth Event Loop Exiting\n");
LOG("DBus Event Loop Exiting\n");
dbus_connection_set_watch_functions(dbt->mConnection,
NULL, NULL, NULL, NULL, NULL);
dbt->TearDownEventLoop();
@ -495,7 +474,7 @@ DBusThread::StartEventLoop()
TearDownData();
return false;
}
LOG("DBus Bluetooth Thread Starting\n");
LOG("DBus Thread Starting\n");
pthread_create(&(mThread), NULL, DBusThread::EventLoop, this);
return true;
}
@ -508,12 +487,12 @@ DBusThread::StopEventLoop()
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");
LOG("Cannot write exit bit to DBus Thread!\n");
}
void *ret;
LOG("DBus Bluetooth Thread Joining\n");
LOG("DBus Thread Joining\n");
pthread_join(mThread, &ret);
LOG("DBus Bluetooth Thread Joined\n");
LOG("DBus Thread Joined\n");
TearDownData();
}
mIsRunning = false;
@ -535,6 +514,7 @@ ConnectDBus(Monitor* aMonitor, bool* aSuccess)
NS_WARNING("Trying to start DBus Thread that is already currently running, skipping.");
return;
}
sDBusThread = new DBusThread();
*aSuccess = true;
if(!sDBusThread->StartEventLoop())
@ -554,6 +534,7 @@ DisconnectDBus(Monitor* aMonitor, bool* aSuccess)
NS_WARNING("Trying to shutdown DBus Thread that is not currently running, skipping.");
return;
}
*aSuccess = true;
sDBusThread->StopEventLoop();
sDBusThread = NULL;

View File

@ -7,18 +7,30 @@
#ifndef mozilla_ipc_dbus_gonk_dbusthread_h__
#define mozilla_ipc_dbus_gonk_dbusthread_h__
struct DBusMessage;
namespace mozilla {
namespace ipc {
class nsCString;
// Starts the DBus thread, which handles returning signals to objects
// that call asynchronous functions. This should be called from the
// main thread at startup.
/**
* Starts the DBus thread, which handles returning signals to objects
* that call asynchronous functions. This should be called from the
* main thread at startup.
*
* @return True on thread starting correctly, false otherwise
*/
bool StartDBus();
// Stop the DBus thread, assuming it's currently running. Should be
// called from main thread.
/**
* Stop the DBus thread, assuming it's currently running. Should be
* called from main thread.
*
* @return True on thread stopping correctly, false otherwise
*/
bool StopDBus();
}
}
#endif

View File

@ -16,8 +16,9 @@
** limitations under the License.
*/
#include <stdio.h>
#include "dbus/dbus.h"
#include "DBusUtils.h"
#include <cstdio>
#include <cstring>
#undef LOG
#if defined(MOZ_WIDGET_GONK)
@ -27,6 +28,10 @@
#define LOG(args...) printf(args);
#endif
#define BLUEZ_DBUS_BASE_PATH "/org/bluez"
#define BLUEZ_DBUS_BASE_IFC "org.bluez"
#define BLUEZ_ERROR_IFC "org.bluez.Error"
namespace mozilla {
namespace ipc {
@ -43,5 +48,216 @@ log_and_free_dbus_error(DBusError* err, const char* function, DBusMessage* msg)
dbus_error_free((err));
}
typedef struct {
void (*user_cb)(DBusMessage *, void *, void *);
void *user;
void *nat;
} dbus_async_call_t;
void dbus_func_args_async_callback(DBusPendingCall *call, void *data) {
dbus_async_call_t *req = (dbus_async_call_t *)data;
DBusMessage *msg;
/* This is guaranteed to be non-NULL, because this function is called only
when once the remote method invokation returns. */
msg = dbus_pending_call_steal_reply(call);
if (msg) {
if (req->user_cb) {
// The user may not deref the message object.
req->user_cb(msg, req->user, req->nat);
}
dbus_message_unref(msg);
}
//dbus_message_unref(req->method);
dbus_pending_call_cancel(call);
dbus_pending_call_unref(call);
free(req);
}
static dbus_bool_t dbus_func_args_async_valist(DBusConnection *conn,
int timeout_ms,
void (*user_cb)(DBusMessage *,
void *,
void*),
void *user,
void *nat,
const char *path,
const char *ifc,
const char *func,
int first_arg_type,
va_list args) {
DBusMessage *msg = NULL;
const char *name;
dbus_async_call_t *pending;
dbus_bool_t reply = FALSE;
/* Compose the command */
msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, path, ifc, func);
if (msg == NULL) {
LOG("Could not allocate D-Bus message object!");
goto done;
}
/* append arguments */
if (!dbus_message_append_args_valist(msg, first_arg_type, args)) {
LOG("Could not append argument to method call!");
goto done;
}
/* Make the call. */
pending = (dbus_async_call_t *)malloc(sizeof(dbus_async_call_t));
if (pending) {
DBusPendingCall *call;
pending->user_cb = user_cb;
pending->user = user;
pending->nat = nat;
//pending->method = msg;
reply = dbus_connection_send_with_reply(conn, msg,
&call,
timeout_ms);
if (reply == TRUE) {
dbus_pending_call_set_notify(call,
dbus_func_args_async_callback,
pending,
NULL);
}
}
done:
if (msg) dbus_message_unref(msg);
return reply;
}
dbus_bool_t dbus_func_args_async(DBusConnection *conn,
int timeout_ms,
void (*reply)(DBusMessage *, void *, void*),
void *user,
void *nat,
const char *path,
const char *ifc,
const char *func,
int first_arg_type,
...) {
dbus_bool_t ret;
va_list lst;
va_start(lst, first_arg_type);
ret = dbus_func_args_async_valist(conn,
timeout_ms,
reply, user, nat,
path, ifc, func,
first_arg_type, lst);
va_end(lst);
return ret;
}
// If err is NULL, then any errors will be LOG'd, and free'd and the reply
// will be NULL.
// If err is not NULL, then it is assumed that dbus_error_init was already
// called, and error's will be returned to the caller without logging. The
// return value is NULL iff an error was set. The client must free the error if
// set.
DBusMessage * dbus_func_args_timeout_valist(DBusConnection *conn,
int timeout_ms,
DBusError *err,
const char *path,
const char *ifc,
const char *func,
int first_arg_type,
va_list args) {
DBusMessage *msg = NULL, *reply = NULL;
const char *name;
bool return_error = (err != NULL);
if (!return_error) {
err = (DBusError*)malloc(sizeof(DBusError));
dbus_error_init(err);
}
/* Compose the command */
msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, path, ifc, func);
if (msg == NULL) {
LOG("Could not allocate D-Bus message object!");
goto done;
}
/* append arguments */
if (!dbus_message_append_args_valist(msg, first_arg_type, args)) {
LOG("Could not append argument to method call!");
goto done;
}
/* Make the call. */
reply = dbus_connection_send_with_reply_and_block(conn, msg, timeout_ms, err);
if (!return_error && dbus_error_is_set(err)) {
//LOG_AND_FREE_DBUS_ERROR_WITH_MSG(err, msg);
}
done:
if (!return_error) {
free(err);
}
if (msg) dbus_message_unref(msg);
return reply;
}
DBusMessage * dbus_func_args_timeout(DBusConnection *conn,
int timeout_ms,
const char *path,
const char *ifc,
const char *func,
int first_arg_type,
...) {
DBusMessage *ret;
va_list lst;
va_start(lst, first_arg_type);
ret = dbus_func_args_timeout_valist(conn, timeout_ms, NULL,
path, ifc, func,
first_arg_type, lst);
va_end(lst);
return ret;
}
DBusMessage * dbus_func_args(DBusConnection *conn,
const char *path,
const char *ifc,
const char *func,
int first_arg_type,
...) {
DBusMessage *ret;
va_list lst;
va_start(lst, first_arg_type);
ret = dbus_func_args_timeout_valist(conn, -1, NULL,
path, ifc, func,
first_arg_type, lst);
va_end(lst);
return ret;
}
DBusMessage * dbus_func_args_error(DBusConnection *conn,
DBusError *err,
const char *path,
const char *ifc,
const char *func,
int first_arg_type,
...) {
DBusMessage *ret;
va_list lst;
va_start(lst, first_arg_type);
ret = dbus_func_args_timeout_valist(conn, -1, err,
path, ifc, func,
first_arg_type, lst);
va_end(lst);
return ret;
}
}
}

View File

@ -19,6 +19,9 @@
#ifndef mozilla_ipc_dbus_dbusutils_h__
#define mozilla_ipc_dbus_dbusutils_h__
#include <dbus/dbus.h>
#include "mozilla/Scoped.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);
@ -29,7 +32,71 @@ struct DBusError;
namespace mozilla {
namespace ipc {
void log_and_free_dbus_error(DBusError* err, const char* function, DBusMessage* msg = NULL);
class DBusMessageRefPtr
{
public:
DBusMessageRefPtr(DBusMessage* aMsg) : mMsg(aMsg)
{
if (mMsg) dbus_message_ref(mMsg);
}
~DBusMessageRefPtr()
{
if (mMsg) dbus_message_unref(mMsg);
}
operator DBusMessage*() { return mMsg; }
DBusMessage* get() { return mMsg; }
private:
DBusMessage* mMsg;
};
void log_and_free_dbus_error(DBusError* err,
const char* function,
DBusMessage* msg = NULL);
dbus_bool_t dbus_func_args_async(DBusConnection *conn,
int timeout_ms,
void (*reply)(DBusMessage *, void *, void *),
void *user,
void *nat,
const char *path,
const char *ifc,
const char *func,
int first_arg_type,
...);
DBusMessage * dbus_func_args(DBusConnection *conn,
const char *path,
const char *ifc,
const char *func,
int first_arg_type,
...);
DBusMessage * dbus_func_args_error(DBusConnection *conn,
DBusError *err,
const char *path,
const char *ifc,
const char *func,
int first_arg_type,
...);
DBusMessage * dbus_func_args_timeout(DBusConnection *conn,
int timeout_ms,
const char *path,
const char *ifc,
const char *func,
int first_arg_type,
...);
DBusMessage * dbus_func_args_timeout_valist(DBusConnection *conn,
int timeout_ms,
DBusError *err,
const char *path,
const char *ifc,
const char *func,
int first_arg_type,
va_list args);
}
}

View File

@ -15,15 +15,19 @@ RawDBusConnection::RawDBusConnection() {
RawDBusConnection::~RawDBusConnection() {
}
bool RawDBusConnection::Create() {
nsresult RawDBusConnection::EstablishDBusConnection() {
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;
return NS_ERROR_FAILURE;
}
dbus_connection_set_exit_on_disconnect(mConnection, FALSE);
return true;
return NS_OK;
}
void RawDBusConnection::ScopedDBusConnectionPtrTraits::release(DBusConnection* ptr)
{
if(ptr) dbus_connection_unref(ptr);
}

View File

@ -12,8 +12,8 @@
#include <stdio.h>
#include <string>
#include <stdlib.h>
#include "nscore.h"
#include "mozilla/Scoped.h"
#include "dbus/dbus.h"
struct DBusConnection;
@ -24,14 +24,13 @@ class RawDBusConnection
{
struct ScopedDBusConnectionPtrTraits : ScopedFreePtrTraits<DBusConnection>
{
static void release(DBusConnection* ptr) { if(ptr) dbus_connection_unref(ptr); }
static void release(DBusConnection* ptr);
};
public:
RawDBusConnection();
~RawDBusConnection();
bool Create();
protected:
nsresult EstablishDBusConnection();
Scoped<ScopedDBusConnectionPtrTraits> mConnection;
};