/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ /* ** 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 #include #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 sDBusConnection; #undef LOG #if defined(MOZ_WIDGET_GONK) #include #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 BluetoothEventObserverTable; static nsAutoPtr sBluetoothEventObserverTable; nsresult RegisterBluetoothEventHandler(const nsCString& aNodeName, BluetoothEventObserver* aHandler) { MOZ_ASSERT(NS_IsMainThread()); BluetoothEventObserverList *ol; NS_ENSURE_TRUE(sBluetoothEventObserverTable, NS_ERROR_FAILURE); 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; NS_ENSURE_TRUE(sBluetoothEventObserverTable, NS_ERROR_FAILURE); 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 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; } } } }