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: /* michael@0: ** Copyright 2006, The Android Open Source Project michael@0: ** michael@0: ** Licensed under the Apache License, Version 2.0 (the "License"); michael@0: ** you may not use this file except in compliance with the License. michael@0: ** You may obtain a copy of the License at michael@0: ** michael@0: ** http://www.apache.org/licenses/LICENSE-2.0 michael@0: ** michael@0: ** Unless required by applicable law or agreed to in writing, software michael@0: ** distributed under the License is distributed on an "AS IS" BASIS, michael@0: ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. michael@0: ** See the License for the specific language governing permissions and michael@0: ** limitations under the License. michael@0: */ michael@0: michael@0: #include "base/basictypes.h" michael@0: #include "BluetoothDBusService.h" michael@0: #include "BluetoothA2dpManager.h" michael@0: #include "BluetoothHfpManager.h" michael@0: #include "BluetoothHidManager.h" michael@0: #include "BluetoothOppManager.h" michael@0: #include "BluetoothProfileController.h" michael@0: #include "BluetoothReplyRunnable.h" michael@0: #include "BluetoothUnixSocketConnector.h" michael@0: #include "BluetoothUtils.h" michael@0: #include "BluetoothUuid.h" michael@0: michael@0: #include michael@0: #include michael@0: michael@0: #include "nsAutoPtr.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsDebug.h" michael@0: #include "nsDataHashtable.h" michael@0: #include "mozilla/ArrayUtils.h" michael@0: #include "mozilla/Atomics.h" michael@0: #include "mozilla/dom/bluetooth/BluetoothTypes.h" michael@0: #include "mozilla/Hal.h" michael@0: #include "mozilla/ipc/UnixSocket.h" michael@0: #include "mozilla/ipc/DBusUtils.h" michael@0: #include "mozilla/ipc/RawDBusConnection.h" michael@0: #include "mozilla/LazyIdleThread.h" michael@0: #include "mozilla/Mutex.h" michael@0: #include "mozilla/NullPtr.h" michael@0: #include "mozilla/StaticMutex.h" michael@0: #include "mozilla/unused.h" michael@0: michael@0: #if defined(MOZ_WIDGET_GONK) michael@0: #include "cutils/properties.h" michael@0: #include michael@0: #endif michael@0: michael@0: /** michael@0: * Some rules for dealing with memory in DBus: michael@0: * - A DBusError only needs to be deleted if it's been set, not just michael@0: * initialized. This is why LOG_AND_FREE... is called only when an error is michael@0: * set, and the macro cleans up the error itself. michael@0: * - A DBusMessage needs to be unrefed when it is newed explicitly. DBusMessages michael@0: * from signals do not need to be unrefed, as they will be cleaned by DBus michael@0: * after DBUS_HANDLER_RESULT_HANDLED is returned from the filter. michael@0: */ michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::ipc; michael@0: USING_BLUETOOTH_NAMESPACE michael@0: michael@0: #define B2G_AGENT_CAPABILITIES "DisplayYesNo" michael@0: #define DBUS_MANAGER_IFACE BLUEZ_DBUS_BASE_IFC ".Manager" michael@0: #define DBUS_ADAPTER_IFACE BLUEZ_DBUS_BASE_IFC ".Adapter" michael@0: #define DBUS_DEVICE_IFACE BLUEZ_DBUS_BASE_IFC ".Device" michael@0: #define DBUS_AGENT_IFACE BLUEZ_DBUS_BASE_IFC ".Agent" michael@0: #define DBUS_SINK_IFACE BLUEZ_DBUS_BASE_IFC ".AudioSink" michael@0: #define DBUS_CTL_IFACE BLUEZ_DBUS_BASE_IFC ".Control" michael@0: #define DBUS_INPUT_IFACE BLUEZ_DBUS_BASE_IFC ".Input" michael@0: #define BLUEZ_DBUS_BASE_PATH "/org/bluez" michael@0: #define BLUEZ_DBUS_BASE_IFC "org.bluez" michael@0: #define BLUEZ_ERROR_IFC "org.bluez.Error" michael@0: michael@0: #define ERR_A2DP_IS_DISCONNECTED "A2dpIsDisconnected" michael@0: #define ERR_AVRCP_IS_DISCONNECTED "AvrcpIsDisconnected" michael@0: michael@0: /** michael@0: * To not lock Bluetooth switch button on Settings UI because of any accident, michael@0: * we will force disabling Bluetooth 5 seconds after the user requesting to michael@0: * turn off Bluetooth. michael@0: */ michael@0: #define TIMEOUT_FORCE_TO_DISABLE_BT 5 michael@0: michael@0: #define BT_LAZY_THREAD_TIMEOUT_MS 3000 michael@0: michael@0: #ifdef MOZ_WIDGET_GONK michael@0: class Bluedroid michael@0: { michael@0: struct ScopedDlHandleTraits michael@0: { michael@0: typedef void* type; michael@0: static void* empty() michael@0: { michael@0: return nullptr; michael@0: } michael@0: static void release(void* handle) michael@0: { michael@0: if (!handle) { michael@0: return; michael@0: } michael@0: int res = dlclose(handle); michael@0: if (res) { michael@0: BT_WARNING("Failed to close libbluedroid.so: %s", dlerror()); michael@0: } michael@0: } michael@0: }; michael@0: michael@0: public: michael@0: Bluedroid() michael@0: : m_bt_enable(nullptr) michael@0: , m_bt_disable(nullptr) michael@0: , m_bt_is_enabled(nullptr) michael@0: {} michael@0: michael@0: bool Enable() michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); // BT thread michael@0: michael@0: if (!mHandle && !Init()) { michael@0: return false; michael@0: } else if (m_bt_is_enabled() == 1) { michael@0: return true; michael@0: } michael@0: // 0 == success, -1 == error michael@0: return !m_bt_enable(); michael@0: } michael@0: michael@0: bool Disable() michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); // BT thread michael@0: michael@0: if (!IsEnabled()) { michael@0: return true; michael@0: } michael@0: // 0 == success, -1 == error michael@0: return !m_bt_disable(); michael@0: } michael@0: michael@0: bool IsEnabled() const michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); // BT thread michael@0: michael@0: if (!mHandle) { michael@0: return false; michael@0: } michael@0: // 1 == enabled, 0 == disabled, -1 == error michael@0: return m_bt_is_enabled() > 0; michael@0: } michael@0: michael@0: private: michael@0: bool Init() michael@0: { michael@0: MOZ_ASSERT(!mHandle); michael@0: michael@0: Scoped handle(dlopen("libbluedroid.so", RTLD_LAZY)); michael@0: if (!handle) { michael@0: BT_WARNING("Failed to open libbluedroid.so: %s", dlerror()); michael@0: return false; michael@0: } michael@0: int (*bt_enable)() = (int (*)())dlsym(handle, "bt_enable"); michael@0: if (!bt_enable) { michael@0: BT_WARNING("Failed to lookup bt_enable: %s", dlerror()); michael@0: return false; michael@0: } michael@0: int (*bt_disable)() = (int (*)())dlsym(handle, "bt_disable"); michael@0: if (!bt_disable) { michael@0: BT_WARNING("Failed to lookup bt_disable: %s", dlerror()); michael@0: return false; michael@0: } michael@0: int (*bt_is_enabled)() = (int (*)())dlsym(handle, "bt_is_enabled"); michael@0: if (!bt_is_enabled) { michael@0: BT_WARNING("Failed to lookup bt_is_enabled: %s", dlerror()); michael@0: return false; michael@0: } michael@0: michael@0: m_bt_enable = bt_enable; michael@0: m_bt_disable = bt_disable; michael@0: m_bt_is_enabled = bt_is_enabled; michael@0: mHandle.reset(handle.forget()); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: Scoped mHandle; michael@0: int (* m_bt_enable)(void); michael@0: int (* m_bt_disable)(void); michael@0: int (* m_bt_is_enabled)(void); michael@0: }; michael@0: michael@0: // michael@0: // BT-thread-only variables michael@0: // michael@0: // The variables below must only be accessed from within the BT thread. michael@0: // michael@0: michael@0: static class Bluedroid sBluedroid; michael@0: #endif michael@0: michael@0: // michael@0: // Read-only constants michael@0: // michael@0: // The constants below are read-only and may be accessed from any michael@0: // thread. Most of the contain DBus state or settings, so keep them michael@0: // on the I/O thread if somehow possible. michael@0: // michael@0: michael@0: typedef struct { michael@0: const char* name; michael@0: int type; michael@0: } Properties; michael@0: michael@0: static const Properties sDeviceProperties[] = { michael@0: {"Address", DBUS_TYPE_STRING}, michael@0: {"Name", DBUS_TYPE_STRING}, michael@0: {"Icon", DBUS_TYPE_STRING}, michael@0: {"Class", DBUS_TYPE_UINT32}, michael@0: {"UUIDs", DBUS_TYPE_ARRAY}, michael@0: {"Paired", DBUS_TYPE_BOOLEAN}, michael@0: {"Connected", DBUS_TYPE_BOOLEAN}, michael@0: {"Trusted", DBUS_TYPE_BOOLEAN}, michael@0: {"Blocked", DBUS_TYPE_BOOLEAN}, michael@0: {"Alias", DBUS_TYPE_STRING}, michael@0: {"Nodes", DBUS_TYPE_ARRAY}, michael@0: {"Adapter", DBUS_TYPE_OBJECT_PATH}, michael@0: {"LegacyPairing", DBUS_TYPE_BOOLEAN}, michael@0: {"RSSI", DBUS_TYPE_INT16}, michael@0: {"TX", DBUS_TYPE_UINT32}, michael@0: {"Type", DBUS_TYPE_STRING}, michael@0: {"Broadcaster", DBUS_TYPE_BOOLEAN}, michael@0: {"Services", DBUS_TYPE_ARRAY} michael@0: }; michael@0: michael@0: static const Properties sAdapterProperties[] = { michael@0: {"Address", DBUS_TYPE_STRING}, michael@0: {"Name", DBUS_TYPE_STRING}, michael@0: {"Class", DBUS_TYPE_UINT32}, michael@0: {"Powered", DBUS_TYPE_BOOLEAN}, michael@0: {"Discoverable", DBUS_TYPE_BOOLEAN}, michael@0: {"DiscoverableTimeout", DBUS_TYPE_UINT32}, michael@0: {"Pairable", DBUS_TYPE_BOOLEAN}, michael@0: {"PairableTimeout", DBUS_TYPE_UINT32}, michael@0: {"Discovering", DBUS_TYPE_BOOLEAN}, michael@0: {"Devices", DBUS_TYPE_ARRAY}, michael@0: {"UUIDs", DBUS_TYPE_ARRAY}, michael@0: {"Type", DBUS_TYPE_STRING} michael@0: }; michael@0: michael@0: static const Properties sManagerProperties[] = { michael@0: {"Adapters", DBUS_TYPE_ARRAY}, michael@0: }; michael@0: michael@0: static const Properties sSinkProperties[] = { michael@0: {"State", DBUS_TYPE_STRING}, michael@0: {"Connected", DBUS_TYPE_BOOLEAN}, michael@0: {"Playing", DBUS_TYPE_BOOLEAN} michael@0: }; michael@0: michael@0: static const Properties sControlProperties[] = { michael@0: {"Connected", DBUS_TYPE_BOOLEAN} michael@0: }; michael@0: michael@0: static const Properties sInputProperties[] = { michael@0: {"Connected", DBUS_TYPE_BOOLEAN} michael@0: }; michael@0: michael@0: static const char* const sBluetoothDBusIfaces[] = { michael@0: DBUS_MANAGER_IFACE, michael@0: DBUS_ADAPTER_IFACE, michael@0: DBUS_DEVICE_IFACE michael@0: }; michael@0: michael@0: static const char* const sBluetoothDBusSignals[] = { michael@0: "type='signal',interface='org.freedesktop.DBus'", michael@0: "type='signal',interface='org.bluez.Adapter'", michael@0: "type='signal',interface='org.bluez.Manager'", michael@0: "type='signal',interface='org.bluez.Device'", michael@0: "type='signal',interface='org.bluez.Input'", michael@0: "type='signal',interface='org.bluez.Network'", michael@0: "type='signal',interface='org.bluez.NetworkServer'", michael@0: "type='signal',interface='org.bluez.HealthDevice'", michael@0: "type='signal',interface='org.bluez.AudioSink'", michael@0: "type='signal',interface='org.bluez.Control'" michael@0: }; michael@0: michael@0: // Only A2DP and HID are authorized. michael@0: static const BluetoothServiceClass sAuthorizedServiceClass[] = { michael@0: BluetoothServiceClass::A2DP, michael@0: BluetoothServiceClass::HID michael@0: }; michael@0: michael@0: /** michael@0: * The adapter name may not be ready whenever event 'AdapterAdded' is received, michael@0: * so we'd like to wait for a bit. Only used on main thread. michael@0: */ michael@0: static const int sWaitingForAdapterNameInterval = 1000; // unit: ms michael@0: michael@0: // michael@0: // main-thread-only variables michael@0: // michael@0: // The variables below must be accessed from within the main thread. michael@0: // michael@0: michael@0: // A queue for connect/disconnect request. See Bug 913372 for details. michael@0: static nsTArray > sControllerArray; michael@0: michael@0: // michael@0: // I/O-thread-only variables michael@0: // michael@0: // The variables below must be accessed from within the I/O thread. michael@0: // michael@0: michael@0: // The DBus connection to the BlueZ daemon michael@0: static StaticAutoPtr sDBusConnection; michael@0: michael@0: // Keep the pairing requests. michael@0: static unsigned int sIsPairing = 0; michael@0: michael@0: static nsDataHashtable* sPairingReqTable; michael@0: michael@0: // The object path of the adapter that should michael@0: // be updated after switching Bluetooth. michael@0: static nsString sAdapterPath; michael@0: michael@0: // michael@0: // The variables below are currently accessed from within multiple michael@0: // threads and should be moved to one specific thread if possible. michael@0: // michael@0: // TODO: Concurrency control is implemented by locking. Maybe there's michael@0: // a non-blocking way to implement this. michael@0: // michael@0: michael@0: // Disconnect all profiles before turning off Bluetooth. Please see Bug 891257 michael@0: // for more details. |sStopBluetoothMonitor| protects access to this variable. michael@0: static int sConnectedDeviceCount = 0; michael@0: static StaticAutoPtr sStopBluetoothMonitor; michael@0: michael@0: // Protects against bug 969447. michael@0: static StaticAutoPtr sGetPropertyMonitor; michael@0: michael@0: typedef void (*UnpackFunc)(DBusMessage*, DBusError*, BluetoothValue&, nsAString&); michael@0: typedef bool (*FilterFunc)(const BluetoothValue&); michael@0: michael@0: static void michael@0: DispatchToDBusThread(Task* task) michael@0: { michael@0: XRE_GetIOMessageLoop()->PostTask(FROM_HERE, task); michael@0: } michael@0: michael@0: static nsresult michael@0: DispatchToBtThread(nsIRunnable* aRunnable) michael@0: { michael@0: /* Due to the fact that the startup and shutdown of the Bluetooth michael@0: * system can take an indefinite amount of time, a separate thread michael@0: * is used for running blocking calls. The thread is not intended michael@0: * for regular Bluetooth operations though. michael@0: */ michael@0: static StaticRefPtr sBluetoothThread; michael@0: michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: if (!sBluetoothThread) { michael@0: sBluetoothThread = new LazyIdleThread(BT_LAZY_THREAD_TIMEOUT_MS, michael@0: NS_LITERAL_CSTRING("BluetoothDBusService"), michael@0: LazyIdleThread::ManualShutdown); michael@0: } michael@0: return sBluetoothThread->Dispatch(aRunnable, NS_DISPATCH_NORMAL); michael@0: } michael@0: michael@0: BluetoothDBusService::BluetoothDBusService() michael@0: { michael@0: sGetPropertyMonitor = new Monitor("BluetoothService.sGetPropertyMonitor"); michael@0: sStopBluetoothMonitor = new Monitor("BluetoothService.sStopBluetoothMonitor"); michael@0: } michael@0: michael@0: BluetoothDBusService::~BluetoothDBusService() michael@0: { michael@0: sStopBluetoothMonitor = nullptr; michael@0: sGetPropertyMonitor = nullptr; michael@0: } michael@0: michael@0: static bool michael@0: GetConnectedDevicesFilter(const BluetoothValue& aValue) michael@0: { michael@0: // We don't have to filter device here michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: GetPairedDevicesFilter(const BluetoothValue& aValue) michael@0: { michael@0: // Check property 'Paired' and only paired device will be returned michael@0: if (aValue.type() != BluetoothValue::TArrayOfBluetoothNamedValue) { michael@0: BT_WARNING("Not a BluetoothNamedValue array!"); michael@0: return false; michael@0: } michael@0: michael@0: const InfallibleTArray& deviceProperties = michael@0: aValue.get_ArrayOfBluetoothNamedValue(); michael@0: uint32_t length = deviceProperties.Length(); michael@0: for (uint32_t p = 0; p < length; ++p) { michael@0: if (deviceProperties[p].name().EqualsLiteral("Paired")) { michael@0: return deviceProperties[p].value().get_bool(); michael@0: } michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: class DistributeBluetoothSignalTask : public nsRunnable michael@0: { michael@0: public: michael@0: DistributeBluetoothSignalTask(const BluetoothSignal& aSignal) michael@0: : mSignal(aSignal) michael@0: { michael@0: } michael@0: michael@0: nsresult Run() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: BluetoothService* bs = BluetoothService::Get(); michael@0: NS_ENSURE_TRUE(bs, NS_ERROR_FAILURE); michael@0: bs->DistributeSignal(mSignal); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: BluetoothSignal mSignal; michael@0: }; michael@0: michael@0: class ControlPropertyChangedHandler : public nsRunnable michael@0: { michael@0: public: michael@0: ControlPropertyChangedHandler(const BluetoothSignal& aSignal) michael@0: : mSignal(aSignal) michael@0: { michael@0: } michael@0: michael@0: nsresult Run() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: if (mSignal.value().type() != BluetoothValue::TArrayOfBluetoothNamedValue) { michael@0: BT_WARNING("Wrong value type for ControlPropertyChangedHandler"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: InfallibleTArray& arr = michael@0: mSignal.value().get_ArrayOfBluetoothNamedValue(); michael@0: MOZ_ASSERT(arr[0].name().EqualsLiteral("Connected")); michael@0: MOZ_ASSERT(arr[0].value().type() == BluetoothValue::Tbool); michael@0: bool connected = arr[0].value().get_bool(); michael@0: michael@0: BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get(); michael@0: NS_ENSURE_TRUE(a2dp, NS_ERROR_FAILURE); michael@0: a2dp->SetAvrcpConnected(connected); michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: BluetoothSignal mSignal; michael@0: }; michael@0: michael@0: class SinkPropertyChangedHandler : public nsRunnable michael@0: { michael@0: public: michael@0: SinkPropertyChangedHandler(const BluetoothSignal& aSignal) michael@0: : mSignal(aSignal) michael@0: { michael@0: } michael@0: michael@0: NS_IMETHOD michael@0: Run() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_ASSERT(mSignal.name().EqualsLiteral("PropertyChanged")); michael@0: MOZ_ASSERT(mSignal.value().type() == michael@0: BluetoothValue::TArrayOfBluetoothNamedValue); michael@0: michael@0: // Replace object path with device address michael@0: nsString address = GetAddressFromObjectPath(mSignal.path()); michael@0: mSignal.path() = address; michael@0: michael@0: BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get(); michael@0: NS_ENSURE_TRUE(a2dp, NS_ERROR_FAILURE); michael@0: a2dp->HandleSinkPropertyChanged(mSignal); michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: BluetoothSignal mSignal; michael@0: }; michael@0: michael@0: class InputPropertyChangedHandler : public nsRunnable michael@0: { michael@0: public: michael@0: InputPropertyChangedHandler(const BluetoothSignal& aSignal) michael@0: : mSignal(aSignal) michael@0: { michael@0: } michael@0: michael@0: NS_IMETHOD michael@0: Run() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_ASSERT(mSignal.name().EqualsLiteral("PropertyChanged")); michael@0: MOZ_ASSERT(mSignal.value().type() == BluetoothValue::TArrayOfBluetoothNamedValue); michael@0: michael@0: // Replace object path with device address michael@0: nsString address = GetAddressFromObjectPath(mSignal.path()); michael@0: mSignal.path() = address; michael@0: michael@0: BluetoothHidManager* hid = BluetoothHidManager::Get(); michael@0: NS_ENSURE_TRUE(hid, NS_ERROR_FAILURE); michael@0: hid->HandleInputPropertyChanged(mSignal); michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: BluetoothSignal mSignal; michael@0: }; michael@0: michael@0: class TryFiringAdapterAddedTask : public Task michael@0: { michael@0: public: michael@0: void Run() MOZ_OVERRIDE michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: BluetoothService* bs = BluetoothService::Get(); michael@0: NS_ENSURE_TRUE_VOID(bs); michael@0: michael@0: bs->AdapterAddedReceived(); michael@0: bs->TryFiringAdapterAdded(); michael@0: } michael@0: }; michael@0: michael@0: class TryFiringAdapterAddedRunnable : public nsRunnable michael@0: { michael@0: public: michael@0: TryFiringAdapterAddedRunnable(bool aDelay) michael@0: : mDelay(aDelay) michael@0: { } michael@0: michael@0: nsresult Run() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: if (mDelay) { michael@0: MessageLoop::current()-> michael@0: PostDelayedTask(FROM_HERE, new TryFiringAdapterAddedTask(), michael@0: sWaitingForAdapterNameInterval); michael@0: } else { michael@0: MessageLoop::current()-> michael@0: PostTask(FROM_HERE, new TryFiringAdapterAddedTask()); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: bool mDelay; michael@0: }; michael@0: michael@0: static bool michael@0: IsDBusMessageError(DBusMessage* aMsg, DBusError* aErr, nsAString& aErrorStr) michael@0: { michael@0: if (aErr && dbus_error_is_set(aErr)) { michael@0: aErrorStr = NS_ConvertUTF8toUTF16(aErr->message); michael@0: LOG_AND_FREE_DBUS_ERROR(aErr); michael@0: return true; michael@0: } michael@0: michael@0: DBusError err; michael@0: dbus_error_init(&err); michael@0: if (dbus_message_get_type(aMsg) == DBUS_MESSAGE_TYPE_ERROR) { michael@0: const char* error_msg; michael@0: if (!dbus_message_get_args(aMsg, &err, DBUS_TYPE_STRING, michael@0: &error_msg, DBUS_TYPE_INVALID) || michael@0: !error_msg) { michael@0: if (dbus_error_is_set(&err)) { michael@0: aErrorStr = NS_ConvertUTF8toUTF16(err.message); michael@0: LOG_AND_FREE_DBUS_ERROR(&err); michael@0: return true; michael@0: } else { michael@0: aErrorStr.AssignLiteral("Unknown Error"); michael@0: return true; michael@0: } michael@0: } else { michael@0: aErrorStr = NS_ConvertUTF8toUTF16(error_msg); michael@0: return true; michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: static void michael@0: UnpackObjectPathMessage(DBusMessage* aMsg, DBusError* aErr, michael@0: BluetoothValue& aValue, nsAString& aErrorStr) michael@0: { michael@0: DBusError err; michael@0: dbus_error_init(&err); michael@0: if (!IsDBusMessageError(aMsg, aErr, aErrorStr)) { michael@0: MOZ_ASSERT(dbus_message_get_type(aMsg) == DBUS_MESSAGE_TYPE_METHOD_RETURN, michael@0: "Got dbus callback that's not a METHOD_RETURN!"); michael@0: const char* object_path; michael@0: if (!dbus_message_get_args(aMsg, &err, DBUS_TYPE_OBJECT_PATH, michael@0: &object_path, DBUS_TYPE_INVALID) || michael@0: !object_path) { michael@0: if (dbus_error_is_set(&err)) { michael@0: aErrorStr = NS_ConvertUTF8toUTF16(err.message); michael@0: LOG_AND_FREE_DBUS_ERROR(&err); michael@0: } michael@0: } else { michael@0: aValue = NS_ConvertUTF8toUTF16(object_path); michael@0: } michael@0: } michael@0: } michael@0: michael@0: class PrepareProfileManagersRunnable : public nsRunnable michael@0: { michael@0: public: michael@0: nsresult Run() michael@0: { michael@0: BluetoothHfpManager* hfp = BluetoothHfpManager::Get(); michael@0: if (!hfp || !hfp->Listen()) { michael@0: BT_WARNING("Failed to start listening for BluetoothHfpManager!"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: BluetoothOppManager* opp = BluetoothOppManager::Get(); michael@0: if (!opp || !opp->Listen()) { michael@0: BT_WARNING("Failed to start listening for BluetoothOppManager!"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get(); michael@0: NS_ENSURE_TRUE(a2dp, NS_ERROR_FAILURE); michael@0: a2dp->Reset(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: }; michael@0: michael@0: static void michael@0: RunDBusCallback(DBusMessage* aMsg, void* aBluetoothReplyRunnable, michael@0: UnpackFunc aFunc) michael@0: { michael@0: #ifdef MOZ_WIDGET_GONK michael@0: // Due to the fact that we're running two dbus loops on desktop implicitly by michael@0: // being gtk based, sometimes we'll get signals/reply coming in on the main michael@0: // thread. There's not a lot we can do about that for the time being and it michael@0: // (technically) shouldn't hurt anything. However, on gonk, die. michael@0: MOZ_ASSERT(!NS_IsMainThread()); // I/O thread michael@0: #endif michael@0: nsRefPtr replyRunnable = michael@0: dont_AddRef(static_cast< BluetoothReplyRunnable* >(aBluetoothReplyRunnable)); michael@0: michael@0: MOZ_ASSERT(replyRunnable, "Callback reply runnable is null!"); michael@0: michael@0: nsAutoString replyError; michael@0: BluetoothValue v; michael@0: aFunc(aMsg, nullptr, v, replyError); michael@0: michael@0: // Bug 941462. When blueZ replys 'I/O error', we treat it as 'internal error'. michael@0: // This usually happned when the first pairing request has not yet finished, michael@0: // the second pairing request issued immediately. michael@0: if (replyError.EqualsLiteral("I/O error")) { michael@0: replyError.AssignLiteral(ERR_INTERNAL_ERROR); michael@0: } michael@0: michael@0: DispatchBluetoothReply(replyRunnable, v, replyError); michael@0: } michael@0: michael@0: static void michael@0: GetObjectPathCallback(DBusMessage* aMsg, void* aBluetoothReplyRunnable) michael@0: { michael@0: if (sIsPairing) { michael@0: RunDBusCallback(aMsg, aBluetoothReplyRunnable, michael@0: UnpackObjectPathMessage); michael@0: sIsPairing--; michael@0: } michael@0: } michael@0: michael@0: static void michael@0: UnpackVoidMessage(DBusMessage* aMsg, DBusError* aErr, BluetoothValue& aValue, michael@0: nsAString& aErrorStr) michael@0: { michael@0: DBusError err; michael@0: dbus_error_init(&err); michael@0: if (!IsDBusMessageError(aMsg, aErr, aErrorStr) && michael@0: dbus_message_get_type(aMsg) == DBUS_MESSAGE_TYPE_METHOD_RETURN && michael@0: !dbus_message_get_args(aMsg, &err, DBUS_TYPE_INVALID)) { michael@0: if (dbus_error_is_set(&err)) { michael@0: aErrorStr = NS_ConvertUTF8toUTF16(err.message); michael@0: LOG_AND_FREE_DBUS_ERROR(&err); michael@0: } michael@0: } michael@0: aValue = aErrorStr.IsEmpty(); michael@0: } michael@0: michael@0: static void michael@0: GetVoidCallback(DBusMessage* aMsg, void* aBluetoothReplyRunnable) michael@0: { michael@0: RunDBusCallback(aMsg, aBluetoothReplyRunnable, michael@0: UnpackVoidMessage); michael@0: } michael@0: michael@0: class ReplyErrorToProfileManager : public nsRunnable michael@0: { michael@0: public: michael@0: ReplyErrorToProfileManager(BluetoothServiceClass aServiceClass, michael@0: bool aConnect, michael@0: const nsAString& aErrorString) michael@0: : mServiceClass(aServiceClass) michael@0: , mConnect(aConnect) michael@0: , mErrorString(aErrorString) michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); // I/O thread michael@0: } michael@0: michael@0: nsresult Run() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: BluetoothProfileManagerBase* profile; michael@0: if (mServiceClass == BluetoothServiceClass::HID) { michael@0: profile = BluetoothHidManager::Get(); michael@0: } else if (mServiceClass == BluetoothServiceClass::A2DP) { michael@0: profile = BluetoothA2dpManager::Get(); michael@0: } else { michael@0: MOZ_ASSERT(false); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: if (mConnect) { michael@0: profile->OnConnect(mErrorString); michael@0: } else { michael@0: profile->OnDisconnect(mErrorString); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: BluetoothServiceClass mServiceClass; michael@0: bool mConnect; michael@0: nsString mErrorString; michael@0: }; michael@0: michael@0: static void michael@0: CheckDBusReply(DBusMessage* aMsg, void* aServiceClass, bool aConnect) michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); // I/O thread michael@0: michael@0: NS_ENSURE_TRUE_VOID(aMsg); michael@0: michael@0: BluetoothValue v; michael@0: nsAutoString replyError; michael@0: UnpackVoidMessage(aMsg, nullptr, v, replyError); michael@0: michael@0: nsAutoPtr serviceClass( michael@0: static_cast(aServiceClass)); michael@0: michael@0: if (!replyError.IsEmpty()) { michael@0: NS_DispatchToMainThread( michael@0: new ReplyErrorToProfileManager(*serviceClass, aConnect, replyError)); michael@0: } michael@0: } michael@0: michael@0: static void michael@0: InputConnectCallback(DBusMessage* aMsg, void* aParam) michael@0: { michael@0: CheckDBusReply(aMsg, aParam, true); michael@0: } michael@0: michael@0: static void michael@0: InputDisconnectCallback(DBusMessage* aMsg, void* aParam) michael@0: { michael@0: CheckDBusReply(aMsg, aParam, false); michael@0: } michael@0: michael@0: static void michael@0: SinkConnectCallback(DBusMessage* aMsg, void* aParam) michael@0: { michael@0: CheckDBusReply(aMsg, aParam, true); michael@0: } michael@0: michael@0: static void michael@0: SinkDisconnectCallback(DBusMessage* aMsg, void* aParam) michael@0: { michael@0: CheckDBusReply(aMsg, aParam, false); michael@0: } michael@0: michael@0: static bool michael@0: HasAudioService(uint32_t aCodValue) michael@0: { michael@0: return ((aCodValue & 0x200000) == 0x200000); michael@0: } michael@0: michael@0: static bool michael@0: ContainsIcon(const InfallibleTArray& aProperties) michael@0: { michael@0: for (uint8_t i = 0; i < aProperties.Length(); i++) { michael@0: if (aProperties[i].name().EqualsLiteral("Icon")) { michael@0: return true; michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: static bool michael@0: GetProperty(DBusMessageIter aIter, const Properties* aPropertyTypes, michael@0: int aPropertyTypeLen, int* aPropIndex, michael@0: InfallibleTArray& aProperties) michael@0: { michael@0: /** michael@0: * Ensure GetProperty runs in critical section otherwise michael@0: * crash due to timing issue occurs when BT is enabled. michael@0: * michael@0: * TODO: Revise GetProperty to solve the crash michael@0: */ michael@0: MonitorAutoLock lock(*sGetPropertyMonitor); michael@0: michael@0: DBusMessageIter prop_val, array_val_iter; michael@0: char* property = nullptr; michael@0: uint32_t array_type; michael@0: int i, expectedType, receivedType; michael@0: michael@0: if (dbus_message_iter_get_arg_type(&aIter) != DBUS_TYPE_STRING) { michael@0: return false; michael@0: } michael@0: michael@0: dbus_message_iter_get_basic(&aIter, &property); michael@0: michael@0: if (!dbus_message_iter_next(&aIter) || michael@0: dbus_message_iter_get_arg_type(&aIter) != DBUS_TYPE_VARIANT) { michael@0: return false; michael@0: } michael@0: michael@0: for (i = 0; i < aPropertyTypeLen; i++) { michael@0: if (!strncmp(property, aPropertyTypes[i].name, strlen(property))) { michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (i == aPropertyTypeLen) { michael@0: BT_LOGR("unknown property: %s", property); michael@0: return false; michael@0: } michael@0: michael@0: nsAutoString propertyName; michael@0: propertyName.AssignASCII(aPropertyTypes[i].name); michael@0: *aPropIndex = i; michael@0: michael@0: // Preprocessing michael@0: dbus_message_iter_recurse(&aIter, &prop_val); michael@0: expectedType = aPropertyTypes[*aPropIndex].type; michael@0: receivedType = dbus_message_iter_get_arg_type(&prop_val); michael@0: michael@0: /** michael@0: * Bug 857896. Since device property "Connected" could be a boolean value or michael@0: * an 2-byte array, we need to check the value type here and convert the michael@0: * first byte into a boolean manually. michael@0: */ michael@0: bool convert = false; michael@0: if (propertyName.EqualsLiteral("Connected") && michael@0: receivedType == DBUS_TYPE_ARRAY) { michael@0: MOZ_ASSERT(aPropertyTypes == sDeviceProperties); michael@0: convert = true; michael@0: } michael@0: michael@0: if ((receivedType != expectedType) && !convert) { michael@0: BT_WARNING("Iterator not type we expect! Property name: %s," michael@0: "Property Type Expected: %d, Property Type Received: %d", michael@0: NS_ConvertUTF16toUTF8(propertyName).get(), expectedType, receivedType); michael@0: return false; michael@0: } michael@0: michael@0: // Extract data michael@0: BluetoothValue propertyValue; michael@0: switch (receivedType) { michael@0: case DBUS_TYPE_STRING: michael@0: case DBUS_TYPE_OBJECT_PATH: michael@0: const char* c; michael@0: dbus_message_iter_get_basic(&prop_val, &c); michael@0: propertyValue = NS_ConvertUTF8toUTF16(c); michael@0: break; michael@0: case DBUS_TYPE_UINT32: michael@0: case DBUS_TYPE_INT16: michael@0: uint32_t i; michael@0: dbus_message_iter_get_basic(&prop_val, &i); michael@0: propertyValue = i; michael@0: break; michael@0: case DBUS_TYPE_BOOLEAN: michael@0: bool b; michael@0: dbus_message_iter_get_basic(&prop_val, &b); michael@0: propertyValue = b; michael@0: break; michael@0: case DBUS_TYPE_ARRAY: michael@0: dbus_message_iter_recurse(&prop_val, &array_val_iter); michael@0: array_type = dbus_message_iter_get_arg_type(&array_val_iter); michael@0: if (array_type == DBUS_TYPE_OBJECT_PATH || michael@0: array_type == DBUS_TYPE_STRING) { michael@0: InfallibleTArray arr; michael@0: do { michael@0: const char* tmp; michael@0: dbus_message_iter_get_basic(&array_val_iter, &tmp); michael@0: nsAutoString s; michael@0: s = NS_ConvertUTF8toUTF16(tmp); michael@0: arr.AppendElement(s); michael@0: } while (dbus_message_iter_next(&array_val_iter)); michael@0: propertyValue = arr; michael@0: } else if (array_type == DBUS_TYPE_BYTE) { michael@0: InfallibleTArray arr; michael@0: do { michael@0: uint8_t tmp; michael@0: dbus_message_iter_get_basic(&array_val_iter, &tmp); michael@0: arr.AppendElement(tmp); michael@0: } while (dbus_message_iter_next(&array_val_iter)); michael@0: propertyValue = arr; michael@0: } else { michael@0: // This happens when the array is 0-length. Apparently we get a michael@0: // DBUS_TYPE_INVALID type. michael@0: propertyValue = InfallibleTArray(); michael@0: } michael@0: break; michael@0: default: michael@0: NS_NOTREACHED("Cannot find dbus message type!"); michael@0: } michael@0: michael@0: // Postprocessing michael@0: if (convert) { michael@0: MOZ_ASSERT(propertyValue.type() == BluetoothValue::TArrayOfuint8_t); michael@0: michael@0: bool b = propertyValue.get_ArrayOfuint8_t()[0]; michael@0: propertyValue = BluetoothValue(b); michael@0: } else if (propertyName.EqualsLiteral("Devices")) { michael@0: MOZ_ASSERT(aPropertyTypes == sAdapterProperties); michael@0: MOZ_ASSERT(propertyValue.type() == BluetoothValue::TArrayOfnsString); michael@0: michael@0: uint32_t length = propertyValue.get_ArrayOfnsString().Length(); michael@0: for (uint32_t i= 0; i < length; i++) { michael@0: nsString& data = propertyValue.get_ArrayOfnsString()[i]; michael@0: data = GetAddressFromObjectPath(data); michael@0: } michael@0: } michael@0: michael@0: aProperties.AppendElement(BluetoothNamedValue(propertyName, propertyValue)); michael@0: return true; michael@0: } michael@0: michael@0: static void michael@0: ParseProperties(DBusMessageIter* aIter, michael@0: BluetoothValue& aValue, michael@0: nsAString& aErrorStr, michael@0: const Properties* aPropertyTypes, michael@0: const int aPropertyTypeLen) michael@0: { michael@0: DBusMessageIter dict_entry, dict; michael@0: int prop_index = -1; michael@0: michael@0: MOZ_ASSERT(dbus_message_iter_get_arg_type(aIter) == DBUS_TYPE_ARRAY, michael@0: "Trying to parse a property from sth. that's not an array"); michael@0: michael@0: dbus_message_iter_recurse(aIter, &dict); michael@0: InfallibleTArray props; michael@0: do { michael@0: MOZ_ASSERT(dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY, michael@0: "Trying to parse a property from sth. that's not an dict!"); michael@0: dbus_message_iter_recurse(&dict, &dict_entry); michael@0: michael@0: if (!GetProperty(dict_entry, aPropertyTypes, aPropertyTypeLen, &prop_index, michael@0: props)) { michael@0: aErrorStr.AssignLiteral("Can't Create Property!"); michael@0: BT_WARNING("Can't create property!"); michael@0: return; michael@0: } michael@0: } while (dbus_message_iter_next(&dict)); michael@0: michael@0: aValue = props; michael@0: } michael@0: michael@0: static bool michael@0: UnpackPropertiesMessage(DBusMessage* aMsg, DBusError* aErr, michael@0: BluetoothValue& aValue, const char* aIface) michael@0: { michael@0: MOZ_ASSERT(aMsg); michael@0: michael@0: const Properties* propertyTypes; michael@0: int propertyTypesLength; michael@0: michael@0: nsAutoString errorStr; michael@0: if (IsDBusMessageError(aMsg, aErr, errorStr) || michael@0: dbus_message_get_type(aMsg) != DBUS_MESSAGE_TYPE_METHOD_RETURN) { michael@0: BT_WARNING("dbus message has an error."); michael@0: return false; michael@0: } michael@0: michael@0: DBusMessageIter iter; michael@0: if (!dbus_message_iter_init(aMsg, &iter)) { michael@0: BT_WARNING("Cannot create dbus message iter!"); michael@0: return false; michael@0: } michael@0: michael@0: if (!strcmp(aIface, DBUS_DEVICE_IFACE)) { michael@0: propertyTypes = sDeviceProperties; michael@0: propertyTypesLength = ArrayLength(sDeviceProperties); michael@0: } else if (!strcmp(aIface, DBUS_ADAPTER_IFACE)) { michael@0: propertyTypes = sAdapterProperties; michael@0: propertyTypesLength = ArrayLength(sAdapterProperties); michael@0: } else if (!strcmp(aIface, DBUS_MANAGER_IFACE)) { michael@0: propertyTypes = sManagerProperties; michael@0: propertyTypesLength = ArrayLength(sManagerProperties); michael@0: } else { michael@0: return false; michael@0: } michael@0: michael@0: ParseProperties(&iter, aValue, errorStr, propertyTypes, michael@0: propertyTypesLength); michael@0: return true; michael@0: } michael@0: michael@0: static void michael@0: ParsePropertyChange(DBusMessage* aMsg, BluetoothValue& aValue, michael@0: nsAString& aErrorStr, const Properties* aPropertyTypes, michael@0: const int aPropertyTypeLen) michael@0: { michael@0: DBusMessageIter iter; michael@0: DBusError err; michael@0: int prop_index = -1; michael@0: InfallibleTArray props; michael@0: michael@0: dbus_error_init(&err); michael@0: if (!dbus_message_iter_init(aMsg, &iter)) { michael@0: BT_WARNING("Can't create iterator!"); michael@0: return; michael@0: } michael@0: michael@0: if (!GetProperty(iter, aPropertyTypes, aPropertyTypeLen, michael@0: &prop_index, props)) { michael@0: BT_WARNING("Can't get property!"); michael@0: aErrorStr.AssignLiteral("Can't get property!"); michael@0: return; michael@0: } michael@0: aValue = props; michael@0: } michael@0: michael@0: class AppendDeviceNameReplyHandler: public DBusReplyHandler michael@0: { michael@0: public: michael@0: AppendDeviceNameReplyHandler(const nsCString& aIface, michael@0: const nsString& aDevicePath, michael@0: const BluetoothSignal& aSignal) michael@0: : mIface(aIface) michael@0: , mDevicePath(aDevicePath) michael@0: , mSignal(aSignal) michael@0: { michael@0: MOZ_ASSERT(!mIface.IsEmpty()); michael@0: MOZ_ASSERT(!mDevicePath.IsEmpty()); michael@0: } michael@0: michael@0: void Handle(DBusMessage* aReply) MOZ_OVERRIDE michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); // I/O thread michael@0: michael@0: if (!aReply || (dbus_message_get_type(aReply) == DBUS_MESSAGE_TYPE_ERROR)) { michael@0: return; michael@0: } michael@0: michael@0: // Get device properties from result of GetProperties michael@0: michael@0: DBusError err; michael@0: dbus_error_init(&err); michael@0: michael@0: BluetoothValue deviceProperties; michael@0: michael@0: bool success = UnpackPropertiesMessage(aReply, &err, deviceProperties, michael@0: mIface.get()); michael@0: if (!success) { michael@0: BT_WARNING("Failed to get device properties"); michael@0: return; michael@0: } michael@0: michael@0: // First we replace object path with device address. michael@0: michael@0: InfallibleTArray& parameters = michael@0: mSignal.value().get_ArrayOfBluetoothNamedValue(); michael@0: nsString address = michael@0: GetAddressFromObjectPath(mDevicePath); michael@0: parameters[0].name().AssignLiteral("address"); michael@0: parameters[0].value() = address; michael@0: michael@0: // Then we append the device's name to the original signal's data. michael@0: michael@0: InfallibleTArray& properties = michael@0: deviceProperties.get_ArrayOfBluetoothNamedValue(); michael@0: uint32_t i; michael@0: for (i = 0; i < properties.Length(); i++) { michael@0: if (properties[i].name().EqualsLiteral("Name")) { michael@0: properties[i].name().AssignLiteral("name"); michael@0: parameters.AppendElement(properties[i]); michael@0: break; michael@0: } michael@0: } michael@0: MOZ_ASSERT_IF(i == properties.Length(), "failed to get device name"); michael@0: michael@0: nsRefPtr task = michael@0: new DistributeBluetoothSignalTask(mSignal); michael@0: NS_DispatchToMainThread(task); michael@0: } michael@0: michael@0: private: michael@0: nsCString mIface; michael@0: nsString mDevicePath; michael@0: BluetoothSignal mSignal; michael@0: }; michael@0: michael@0: static void michael@0: AppendDeviceName(BluetoothSignal& aSignal) michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); // I/O thread michael@0: MOZ_ASSERT(sDBusConnection); michael@0: michael@0: BluetoothValue v = aSignal.value(); michael@0: if (v.type() != BluetoothValue::TArrayOfBluetoothNamedValue || michael@0: v.get_ArrayOfBluetoothNamedValue().Length() == 0) { michael@0: BT_WARNING("Invalid argument type for AppendDeviceNameRunnable"); michael@0: return; michael@0: } michael@0: const InfallibleTArray& arr = michael@0: v.get_ArrayOfBluetoothNamedValue(); michael@0: michael@0: // Device object path should be put in the first element michael@0: if (!arr[0].name().EqualsLiteral("path") || michael@0: arr[0].value().type() != BluetoothValue::TnsString) { michael@0: BT_WARNING("Invalid object path for AppendDeviceNameRunnable"); michael@0: return; michael@0: } michael@0: michael@0: nsString devicePath = arr[0].value().get_nsString(); michael@0: michael@0: nsRefPtr handler = michael@0: new AppendDeviceNameReplyHandler(nsCString(DBUS_DEVICE_IFACE), michael@0: devicePath, aSignal); michael@0: michael@0: bool success = sDBusConnection->SendWithReply( michael@0: AppendDeviceNameReplyHandler::Callback, handler.get(), 1000, michael@0: BLUEZ_DBUS_BASE_IFC, NS_ConvertUTF16toUTF8(devicePath).get(), michael@0: DBUS_DEVICE_IFACE, "GetProperties", DBUS_TYPE_INVALID); michael@0: michael@0: NS_ENSURE_TRUE_VOID(success); michael@0: michael@0: unused << handler.forget(); // picked up by callback handler michael@0: } michael@0: michael@0: class SetPairingConfirmationTask : public Task michael@0: { michael@0: public: michael@0: SetPairingConfirmationTask(const nsAString& aDeviceAddress, michael@0: bool aConfirm, michael@0: BluetoothReplyRunnable* aRunnable) michael@0: : mDeviceAddress(aDeviceAddress) michael@0: , mConfirm(aConfirm) michael@0: , mRunnable(aRunnable) michael@0: { michael@0: MOZ_ASSERT(!mDeviceAddress.IsEmpty()); michael@0: } michael@0: michael@0: void Run() MOZ_OVERRIDE michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); // I/O thread michael@0: MOZ_ASSERT(sDBusConnection); michael@0: michael@0: nsAutoString errorStr; michael@0: BluetoothValue v = true; michael@0: DBusMessage *msg; michael@0: michael@0: if (!sPairingReqTable->Get(mDeviceAddress, &msg) && mRunnable) { michael@0: BT_WARNING("%s: Couldn't get original request message.", __FUNCTION__); michael@0: errorStr.AssignLiteral("Couldn't get original request message."); michael@0: DispatchBluetoothReply(mRunnable, v, errorStr); michael@0: michael@0: return; michael@0: } michael@0: michael@0: DBusMessage *reply; michael@0: michael@0: if (mConfirm) { michael@0: reply = dbus_message_new_method_return(msg); michael@0: } else { michael@0: reply = dbus_message_new_error(msg, "org.bluez.Error.Rejected", michael@0: "User rejected confirmation"); michael@0: } michael@0: michael@0: if (!reply) { michael@0: BT_WARNING("%s: Memory can't be allocated for the message.", __FUNCTION__); michael@0: dbus_message_unref(msg); michael@0: errorStr.AssignLiteral("Memory can't be allocated for the message."); michael@0: if (mRunnable) { michael@0: DispatchBluetoothReply(mRunnable, v, errorStr); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: bool result = sDBusConnection->Send(reply); michael@0: if (!result) { michael@0: errorStr.AssignLiteral("Can't send message!"); michael@0: } michael@0: michael@0: dbus_message_unref(msg); michael@0: dbus_message_unref(reply); michael@0: sPairingReqTable->Remove(mDeviceAddress); michael@0: if (mRunnable) { michael@0: DispatchBluetoothReply(mRunnable, v, errorStr); michael@0: } michael@0: } michael@0: michael@0: private: michael@0: nsString mDeviceAddress; michael@0: bool mConfirm; michael@0: nsRefPtr mRunnable; michael@0: }; michael@0: michael@0: static DBusHandlerResult michael@0: AgentEventFilter(DBusConnection *conn, DBusMessage *msg, void *data) michael@0: { michael@0: if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_METHOD_CALL) { michael@0: BT_WARNING("%s: agent handler not interested (not a method call).\n", michael@0: __FUNCTION__); michael@0: return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; michael@0: } michael@0: michael@0: DBusError err; michael@0: dbus_error_init(&err); michael@0: michael@0: BT_LOGD("%s: %s, %s", __FUNCTION__, michael@0: dbus_message_get_path(msg), michael@0: dbus_message_get_member(msg)); michael@0: michael@0: nsString signalPath = NS_ConvertUTF8toUTF16(dbus_message_get_path(msg)); michael@0: nsString signalName = NS_ConvertUTF8toUTF16(dbus_message_get_member(msg)); michael@0: nsString errorStr; michael@0: BluetoothValue v; michael@0: InfallibleTArray parameters; michael@0: bool isPairingReq = false; michael@0: BluetoothSignal signal(signalName, signalPath, v); michael@0: char *objectPath; michael@0: michael@0: // The following descriptions of each signal are retrieved from: michael@0: // michael@0: // http://maemo.org/api_refs/5.0/beta/bluez/agent.html michael@0: // michael@0: if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, "Cancel")) { michael@0: // This method gets called to indicate that the agent request failed before michael@0: // a reply was returned. michael@0: michael@0: // Return directly michael@0: DBusMessage *reply = dbus_message_new_method_return(msg); michael@0: michael@0: if (!reply) { michael@0: errorStr.AssignLiteral("Memory can't be allocated for the message."); michael@0: goto handle_error; michael@0: } michael@0: michael@0: dbus_connection_send(conn, reply, nullptr); michael@0: dbus_message_unref(reply); michael@0: v = parameters; michael@0: } else if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, "Authorize")) { michael@0: // This method gets called when the service daemon needs to authorize a michael@0: // connection/service request. michael@0: const char *uuid; michael@0: if (!dbus_message_get_args(msg, nullptr, michael@0: DBUS_TYPE_OBJECT_PATH, &objectPath, michael@0: DBUS_TYPE_STRING, &uuid, michael@0: DBUS_TYPE_INVALID)) { michael@0: errorStr.AssignLiteral("Invalid arguments for Authorize() method"); michael@0: goto handle_error; michael@0: } michael@0: michael@0: NS_ConvertUTF8toUTF16 uuidStr(uuid); michael@0: BluetoothServiceClass serviceClass = michael@0: BluetoothUuidHelper::GetBluetoothServiceClass(uuidStr); michael@0: if (serviceClass == BluetoothServiceClass::UNKNOWN) { michael@0: errorStr.AssignLiteral("Failed to get service class"); michael@0: goto handle_error; michael@0: } michael@0: michael@0: DBusMessage* reply = nullptr; michael@0: uint32_t i; michael@0: for (i = 0; i < MOZ_ARRAY_LENGTH(sAuthorizedServiceClass); i++) { michael@0: if (serviceClass == sAuthorizedServiceClass[i]) { michael@0: reply = dbus_message_new_method_return(msg); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: // The uuid isn't authorized michael@0: if (i == MOZ_ARRAY_LENGTH(sAuthorizedServiceClass)) { michael@0: BT_WARNING("Uuid is not authorized."); michael@0: reply = dbus_message_new_error(msg, "org.bluez.Error.Rejected", michael@0: "The uuid is not authorized"); michael@0: } michael@0: michael@0: if (!reply) { michael@0: errorStr.AssignLiteral("Memory can't be allocated for the message."); michael@0: goto handle_error; michael@0: } michael@0: michael@0: dbus_connection_send(conn, reply, nullptr); michael@0: dbus_message_unref(reply); michael@0: return DBUS_HANDLER_RESULT_HANDLED; michael@0: } else if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, michael@0: "RequestConfirmation")) { michael@0: // This method gets called when the service daemon needs to confirm a michael@0: // passkey for an authentication. michael@0: uint32_t passkey; michael@0: if (!dbus_message_get_args(msg, nullptr, michael@0: DBUS_TYPE_OBJECT_PATH, &objectPath, michael@0: DBUS_TYPE_UINT32, &passkey, michael@0: DBUS_TYPE_INVALID)) { michael@0: errorStr.AssignLiteral("Invalid arguments: RequestConfirmation()"); michael@0: goto handle_error; michael@0: } michael@0: michael@0: parameters.AppendElement( michael@0: BluetoothNamedValue(NS_LITERAL_STRING("path"), michael@0: NS_ConvertUTF8toUTF16(objectPath))); michael@0: parameters.AppendElement( michael@0: BluetoothNamedValue(NS_LITERAL_STRING("method"), michael@0: NS_LITERAL_STRING("confirmation"))); michael@0: parameters.AppendElement( michael@0: BluetoothNamedValue(NS_LITERAL_STRING("passkey"), passkey)); michael@0: michael@0: v = parameters; michael@0: isPairingReq = true; michael@0: } else if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, michael@0: "RequestPinCode")) { michael@0: // This method gets called when the service daemon needs to get the passkey michael@0: // for an authentication. The return value should be a string of 1-16 michael@0: // characters length. The string can be alphanumeric. michael@0: if (!dbus_message_get_args(msg, nullptr, michael@0: DBUS_TYPE_OBJECT_PATH, &objectPath, michael@0: DBUS_TYPE_INVALID)) { michael@0: errorStr.AssignLiteral("Invalid arguments for RequestPinCode() method"); michael@0: goto handle_error; michael@0: } michael@0: michael@0: parameters.AppendElement( michael@0: BluetoothNamedValue(NS_LITERAL_STRING("path"), michael@0: NS_ConvertUTF8toUTF16(objectPath))); michael@0: parameters.AppendElement( michael@0: BluetoothNamedValue(NS_LITERAL_STRING("method"), michael@0: NS_LITERAL_STRING("pincode"))); michael@0: michael@0: v = parameters; michael@0: isPairingReq = true; michael@0: } else if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, michael@0: "RequestPasskey")) { michael@0: // This method gets called when the service daemon needs to get the passkey michael@0: // for an authentication. The return value should be a numeric value michael@0: // between 0-999999. michael@0: if (!dbus_message_get_args(msg, nullptr, michael@0: DBUS_TYPE_OBJECT_PATH, &objectPath, michael@0: DBUS_TYPE_INVALID)) { michael@0: errorStr.AssignLiteral("Invalid arguments for RequestPasskey() method"); michael@0: goto handle_error; michael@0: } michael@0: michael@0: parameters.AppendElement(BluetoothNamedValue( michael@0: NS_LITERAL_STRING("path"), michael@0: NS_ConvertUTF8toUTF16(objectPath))); michael@0: parameters.AppendElement(BluetoothNamedValue( michael@0: NS_LITERAL_STRING("method"), michael@0: NS_LITERAL_STRING("passkey"))); michael@0: michael@0: v = parameters; michael@0: isPairingReq = true; michael@0: } else if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, "Release")) { michael@0: // This method gets called when the service daemon unregisters the agent. michael@0: // An agent can use it to do cleanup tasks. There is no need to unregister michael@0: // the agent, because when this method gets called it has already been michael@0: // unregistered. michael@0: DBusMessage *reply = dbus_message_new_method_return(msg); michael@0: michael@0: if (!reply) { michael@0: errorStr.AssignLiteral("Memory can't be allocated for the message."); michael@0: goto handle_error; michael@0: } michael@0: michael@0: dbus_connection_send(conn, reply, nullptr); michael@0: dbus_message_unref(reply); michael@0: michael@0: // Do not send a notification to upper layer, too annoying. michael@0: return DBUS_HANDLER_RESULT_HANDLED; michael@0: } else if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, "RequestPairingConsent")) { michael@0: // Directly SetPairingconfirmation for RequestPairingConsent here michael@0: if (!dbus_message_get_args(msg, nullptr, michael@0: DBUS_TYPE_OBJECT_PATH, &objectPath, michael@0: DBUS_TYPE_INVALID)) { michael@0: errorStr.AssignLiteral("Invalid arguments: RequestPairingConsent()"); michael@0: goto handle_error; michael@0: } michael@0: michael@0: nsString address = GetAddressFromObjectPath(NS_ConvertUTF8toUTF16(objectPath)); michael@0: sPairingReqTable->Put(address, msg); michael@0: Task* task = new SetPairingConfirmationTask(address, true, nullptr); michael@0: DispatchToDBusThread(task); michael@0: // Increase dbus message reference counts, it will be decreased in michael@0: // SetPairingConfirmationTask michael@0: dbus_message_ref(msg); michael@0: // Do not send a notification to upper layer michael@0: return DBUS_HANDLER_RESULT_HANDLED; michael@0: } else { michael@0: #ifdef DEBUG michael@0: BT_WARNING("agent handler %s: Unhandled event. Ignore.", __FUNCTION__); michael@0: #endif michael@0: return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; michael@0: } michael@0: michael@0: if (!errorStr.IsEmpty()) { michael@0: BT_WARNING(NS_ConvertUTF16toUTF8(errorStr).get()); michael@0: return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; michael@0: } michael@0: michael@0: // Update value after parsing DBus message michael@0: signal.value() = v; michael@0: michael@0: if (isPairingReq) { michael@0: sPairingReqTable->Put( michael@0: GetAddressFromObjectPath(NS_ConvertUTF8toUTF16(objectPath)), msg); michael@0: michael@0: // Increase ref count here because we need this message later. michael@0: // It'll be unrefed when set*Internal() is called. michael@0: dbus_message_ref(msg); michael@0: michael@0: AppendDeviceName(signal); michael@0: } else { michael@0: NS_DispatchToMainThread(new DistributeBluetoothSignalTask(signal)); michael@0: } michael@0: michael@0: return DBUS_HANDLER_RESULT_HANDLED; michael@0: michael@0: handle_error: michael@0: BT_WARNING(NS_ConvertUTF16toUTF8(errorStr).get()); michael@0: return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; michael@0: } michael@0: michael@0: class RegisterAgentReplyHandler : public DBusReplyHandler michael@0: { michael@0: public: michael@0: RegisterAgentReplyHandler(const DBusObjectPathVTable* aAgentVTable) michael@0: : mAgentVTable(aAgentVTable) michael@0: { michael@0: MOZ_ASSERT(aAgentVTable); michael@0: } michael@0: michael@0: void Handle(DBusMessage* aReply) michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); // I/O thread michael@0: MOZ_ASSERT(sDBusConnection); michael@0: michael@0: if (!aReply || (dbus_message_get_type(aReply) == DBUS_MESSAGE_TYPE_ERROR)) { michael@0: return; michael@0: } michael@0: michael@0: // There is no "RegisterAgent" function defined in device interface. michael@0: // When we call "CreatePairedDevice", it will do device agent registration michael@0: // for us. (See maemo.org/api_refs/5.0/beta/bluez/adapter.html) michael@0: if (!dbus_connection_register_object_path(sDBusConnection->GetConnection(), michael@0: KEY_REMOTE_AGENT, michael@0: mAgentVTable, michael@0: nullptr)) { michael@0: BT_WARNING("%s: Can't register object path %s for remote device agent!", michael@0: __FUNCTION__, KEY_REMOTE_AGENT); michael@0: return; michael@0: } michael@0: michael@0: NS_DispatchToMainThread(new PrepareProfileManagersRunnable()); michael@0: } michael@0: michael@0: private: michael@0: const DBusObjectPathVTable* mAgentVTable; michael@0: }; michael@0: michael@0: class AddReservedServiceRecordsReplyHandler : public DBusReplyHandler michael@0: { michael@0: public: michael@0: void Handle(DBusMessage* aReply) michael@0: { michael@0: static const DBusObjectPathVTable sAgentVTable = { michael@0: nullptr, AgentEventFilter, nullptr, nullptr, nullptr, nullptr michael@0: }; michael@0: michael@0: MOZ_ASSERT(!NS_IsMainThread()); // I/O thread michael@0: michael@0: if (!aReply || (dbus_message_get_type(aReply) == DBUS_MESSAGE_TYPE_ERROR)) { michael@0: return; michael@0: } michael@0: michael@0: // TODO/qdot: This needs to be held for the life of the bluetooth connection michael@0: // so we could clean it up. For right now though, we can throw it away. michael@0: nsTArray handles; michael@0: michael@0: ExtractHandles(aReply, handles); michael@0: michael@0: if(!RegisterAgent(&sAgentVTable)) { michael@0: BT_WARNING("Failed to register agent"); michael@0: } michael@0: } michael@0: michael@0: private: michael@0: void ExtractHandles(DBusMessage *aMessage, nsTArray& aOutHandles) michael@0: { michael@0: DBusError error; michael@0: int length; michael@0: uint32_t* handles = nullptr; michael@0: michael@0: dbus_error_init(&error); michael@0: michael@0: bool success = dbus_message_get_args(aMessage, &error, michael@0: DBUS_TYPE_ARRAY, michael@0: DBUS_TYPE_UINT32, michael@0: &handles, &length, michael@0: DBUS_TYPE_INVALID); michael@0: if (success != TRUE) { michael@0: LOG_AND_FREE_DBUS_ERROR(&error); michael@0: return; michael@0: } michael@0: michael@0: if (!handles) { michael@0: BT_WARNING("Null array in extract_handles"); michael@0: return; michael@0: } michael@0: michael@0: for (int i = 0; i < length; ++i) { michael@0: aOutHandles.AppendElement(handles[i]); michael@0: } michael@0: } michael@0: michael@0: bool RegisterAgent(const DBusObjectPathVTable* aAgentVTable) michael@0: { michael@0: const char* agentPath = KEY_LOCAL_AGENT; michael@0: const char* capabilities = B2G_AGENT_CAPABILITIES; michael@0: michael@0: MOZ_ASSERT(sDBusConnection); michael@0: michael@0: // Local agent means agent for Adapter, not agent for Device. Some signals michael@0: // will be passed to local agent, some will be passed to device agent. michael@0: // For example, if a remote device would like to pair with us, then the michael@0: // signal will be passed to local agent. If we start pairing process with michael@0: // calling CreatePairedDevice, we'll get signal which should be passed to michael@0: // device agent. michael@0: if (!dbus_connection_register_object_path(sDBusConnection->GetConnection(), michael@0: KEY_LOCAL_AGENT, michael@0: aAgentVTable, michael@0: nullptr)) { michael@0: BT_WARNING("%s: Can't register object path %s for agent!", michael@0: __FUNCTION__, KEY_LOCAL_AGENT); michael@0: return false; michael@0: } michael@0: michael@0: nsRefPtr handler = michael@0: new RegisterAgentReplyHandler(aAgentVTable); michael@0: MOZ_ASSERT(!sAdapterPath.IsEmpty()); michael@0: michael@0: bool success = sDBusConnection->SendWithReply( michael@0: RegisterAgentReplyHandler::Callback, handler.get(), -1, michael@0: BLUEZ_DBUS_BASE_IFC, michael@0: NS_ConvertUTF16toUTF8(sAdapterPath).get(), michael@0: DBUS_ADAPTER_IFACE, "RegisterAgent", michael@0: DBUS_TYPE_OBJECT_PATH, &agentPath, michael@0: DBUS_TYPE_STRING, &capabilities, michael@0: DBUS_TYPE_INVALID); michael@0: michael@0: NS_ENSURE_TRUE(success, false); michael@0: michael@0: unused << handler.forget(); // picked up by callback handler michael@0: michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: class AddReservedServiceRecordsTask : public Task michael@0: { michael@0: public: michael@0: AddReservedServiceRecordsTask() michael@0: { } michael@0: michael@0: void Run() michael@0: { michael@0: static const dbus_uint32_t sServices[] = { michael@0: BluetoothServiceClass::HANDSFREE_AG, michael@0: BluetoothServiceClass::HEADSET_AG, michael@0: BluetoothServiceClass::OBJECT_PUSH michael@0: }; michael@0: michael@0: MOZ_ASSERT(!NS_IsMainThread()); // I/O thread michael@0: MOZ_ASSERT(sDBusConnection); michael@0: MOZ_ASSERT(!sAdapterPath.IsEmpty()); michael@0: michael@0: nsRefPtr handler = michael@0: new AddReservedServiceRecordsReplyHandler(); michael@0: michael@0: const dbus_uint32_t* services = sServices; michael@0: michael@0: bool success = sDBusConnection->SendWithReply( michael@0: DBusReplyHandler::Callback, handler.get(), -1, michael@0: BLUEZ_DBUS_BASE_IFC, michael@0: NS_ConvertUTF16toUTF8(sAdapterPath).get(), michael@0: DBUS_ADAPTER_IFACE, "AddReservedServiceRecords", michael@0: DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, michael@0: &services, ArrayLength(sServices), DBUS_TYPE_INVALID); michael@0: michael@0: NS_ENSURE_TRUE_VOID(success); michael@0: michael@0: unused << handler.forget(); /* picked up by callback handler */ michael@0: } michael@0: }; michael@0: michael@0: class PrepareAdapterRunnable : public nsRunnable michael@0: { michael@0: public: michael@0: PrepareAdapterRunnable() michael@0: { } michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: Task* task = new AddReservedServiceRecordsTask(); michael@0: DispatchToDBusThread(task); michael@0: michael@0: return NS_OK; michael@0: } michael@0: }; michael@0: michael@0: class RequestPlayStatusTask : public nsRunnable michael@0: { michael@0: public: michael@0: RequestPlayStatusTask() michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); // I/O thread michael@0: } michael@0: michael@0: nsresult Run() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: BluetoothSignal signal(NS_LITERAL_STRING(REQUEST_MEDIA_PLAYSTATUS_ID), michael@0: NS_LITERAL_STRING(KEY_ADAPTER), michael@0: InfallibleTArray()); michael@0: michael@0: BluetoothService* bs = BluetoothService::Get(); michael@0: NS_ENSURE_TRUE(bs, NS_ERROR_FAILURE); michael@0: bs->DistributeSignal(signal); michael@0: michael@0: return NS_OK; michael@0: } michael@0: }; michael@0: michael@0: // Called by dbus during WaitForAndDispatchEventNative() michael@0: // This function is called on the IOThread michael@0: static DBusHandlerResult michael@0: EventFilter(DBusConnection* aConn, DBusMessage* aMsg, void* aData) michael@0: { michael@0: // I/O thread michael@0: MOZ_ASSERT(!NS_IsMainThread(), "Shouldn't be called from Main Thread!"); michael@0: michael@0: if (dbus_message_get_type(aMsg) != DBUS_MESSAGE_TYPE_SIGNAL) { michael@0: BT_WARNING("%s: event handler not interested in %s (not a signal).\n", michael@0: __FUNCTION__, dbus_message_get_member(aMsg)); michael@0: return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; michael@0: } michael@0: michael@0: if (dbus_message_get_path(aMsg) == nullptr) { michael@0: BT_WARNING("DBusMessage %s has no bluetooth destination, ignoring\n", michael@0: dbus_message_get_member(aMsg)); michael@0: return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; michael@0: } michael@0: michael@0: DBusError err; michael@0: dbus_error_init(&err); michael@0: michael@0: nsAutoString signalPath; michael@0: nsAutoString signalName; michael@0: nsAutoString signalInterface; michael@0: michael@0: BT_LOGD("%s: %s, %s, %s", __FUNCTION__, michael@0: dbus_message_get_interface(aMsg), michael@0: dbus_message_get_path(aMsg), michael@0: dbus_message_get_member(aMsg)); michael@0: michael@0: signalInterface = NS_ConvertUTF8toUTF16(dbus_message_get_interface(aMsg)); michael@0: signalPath = NS_ConvertUTF8toUTF16(dbus_message_get_path(aMsg)); michael@0: signalName = NS_ConvertUTF8toUTF16(dbus_message_get_member(aMsg)); michael@0: nsString errorStr; michael@0: BluetoothValue v; michael@0: michael@0: // Since the signalPath extracted from dbus message is a object path, michael@0: // we'd like to re-assign them to corresponding key entry in michael@0: // BluetoothSignalObserverTable michael@0: if (signalInterface.EqualsLiteral(DBUS_MANAGER_IFACE)) { michael@0: signalPath.AssignLiteral(KEY_MANAGER); michael@0: } else if (signalInterface.EqualsLiteral(DBUS_ADAPTER_IFACE)) { michael@0: signalPath.AssignLiteral(KEY_ADAPTER); michael@0: } else if (signalInterface.EqualsLiteral(DBUS_DEVICE_IFACE)){ michael@0: signalPath = GetAddressFromObjectPath(signalPath); michael@0: } michael@0: michael@0: if (dbus_message_is_signal(aMsg, DBUS_ADAPTER_IFACE, "DeviceFound")) { michael@0: DBusMessageIter iter; michael@0: michael@0: if (!dbus_message_iter_init(aMsg, &iter)) { michael@0: BT_WARNING("Can't create iterator!"); michael@0: return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; michael@0: } michael@0: michael@0: const char* addr; michael@0: dbus_message_iter_get_basic(&iter, &addr); michael@0: michael@0: if (!dbus_message_iter_next(&iter)) { michael@0: errorStr.AssignLiteral("Unexpected message struct in msg DeviceFound"); michael@0: } else { michael@0: ParseProperties(&iter, michael@0: v, michael@0: errorStr, michael@0: sDeviceProperties, michael@0: ArrayLength(sDeviceProperties)); michael@0: michael@0: InfallibleTArray& properties = michael@0: v.get_ArrayOfBluetoothNamedValue(); michael@0: michael@0: // The DBus DeviceFound message actually passes back a key value object michael@0: // with the address as the key and the rest of the device properties as michael@0: // a dict value. After we parse out the properties, we need to go back michael@0: // and add the address to the ipdl dict we've created to make sure we michael@0: // have all of the information to correctly build the device. michael@0: nsAutoString address = NS_ConvertUTF8toUTF16(addr); michael@0: properties.AppendElement( michael@0: BluetoothNamedValue(NS_LITERAL_STRING("Address"), address)); michael@0: properties.AppendElement( michael@0: BluetoothNamedValue(NS_LITERAL_STRING("Path"), michael@0: GetObjectPathFromAddress(signalPath, address))); michael@0: michael@0: if (!ContainsIcon(properties)) { michael@0: for (uint32_t i = 0; i < properties.Length(); i++) { michael@0: // It is possible that property Icon missed due to CoD of major michael@0: // class is TOY but service class is "Audio", we need to assign michael@0: // Icon as audio-card. This is for PTS test TC_AG_COD_BV_02_I. michael@0: // As HFP specification defined that michael@0: // service class is "Audio" can be considered as HFP AG. michael@0: if (properties[i].name().EqualsLiteral("Class")) { michael@0: if (HasAudioService(properties[i].value().get_uint32_t())) { michael@0: v.get_ArrayOfBluetoothNamedValue().AppendElement( michael@0: BluetoothNamedValue(NS_LITERAL_STRING("Icon"), michael@0: NS_LITERAL_STRING("audio-card"))); michael@0: } michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: } else if (dbus_message_is_signal(aMsg, DBUS_ADAPTER_IFACE, michael@0: "DeviceDisappeared")) { michael@0: const char* str; michael@0: if (!dbus_message_get_args(aMsg, &err, michael@0: DBUS_TYPE_STRING, &str, michael@0: DBUS_TYPE_INVALID)) { michael@0: LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, aMsg); michael@0: errorStr.AssignLiteral("Cannot parse device address!"); michael@0: } else { michael@0: v = NS_ConvertUTF8toUTF16(str); michael@0: } michael@0: } else if (dbus_message_is_signal(aMsg, DBUS_ADAPTER_IFACE, michael@0: "DeviceCreated")) { michael@0: const char* str; michael@0: if (!dbus_message_get_args(aMsg, &err, michael@0: DBUS_TYPE_OBJECT_PATH, &str, michael@0: DBUS_TYPE_INVALID)) { michael@0: LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, aMsg); michael@0: errorStr.AssignLiteral("Cannot parse device path!"); michael@0: } else { michael@0: v = NS_ConvertUTF8toUTF16(str); michael@0: } michael@0: } else if (dbus_message_is_signal(aMsg, DBUS_ADAPTER_IFACE, michael@0: "DeviceRemoved")) { michael@0: const char* str; michael@0: if (!dbus_message_get_args(aMsg, &err, michael@0: DBUS_TYPE_OBJECT_PATH, &str, michael@0: DBUS_TYPE_INVALID)) { michael@0: LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, aMsg); michael@0: errorStr.AssignLiteral("Cannot parse device path!"); michael@0: } else { michael@0: v = NS_ConvertUTF8toUTF16(str); michael@0: } michael@0: } else if (dbus_message_is_signal(aMsg, DBUS_ADAPTER_IFACE, michael@0: "PropertyChanged")) { michael@0: ParsePropertyChange(aMsg, michael@0: v, michael@0: errorStr, michael@0: sAdapterProperties, michael@0: ArrayLength(sAdapterProperties)); michael@0: } else if (dbus_message_is_signal(aMsg, DBUS_DEVICE_IFACE, michael@0: "PropertyChanged")) { michael@0: ParsePropertyChange(aMsg, michael@0: v, michael@0: errorStr, michael@0: sDeviceProperties, michael@0: ArrayLength(sDeviceProperties)); michael@0: michael@0: if (v.type() == BluetoothValue::T__None) { michael@0: BT_WARNING("PropertyChanged event couldn't be parsed."); michael@0: return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; michael@0: } michael@0: michael@0: BluetoothNamedValue& property = v.get_ArrayOfBluetoothNamedValue()[0]; michael@0: if (property.name().EqualsLiteral("Paired")) { michael@0: // Original approach: Broadcast system message of michael@0: // "bluetooth-pairedstatuschanged" from BluetoothService. michael@0: BluetoothValue newValue(v); michael@0: ToLowerCase(newValue.get_ArrayOfBluetoothNamedValue()[0].name()); michael@0: BluetoothSignal signal(NS_LITERAL_STRING(PAIRED_STATUS_CHANGED_ID), michael@0: NS_LITERAL_STRING(KEY_LOCAL_AGENT), michael@0: newValue); michael@0: NS_DispatchToMainThread(new DistributeBluetoothSignalTask(signal)); michael@0: michael@0: // New approach: Dispatch event from BluetoothAdapter michael@0: bool status = property.value(); michael@0: InfallibleTArray parameters; michael@0: parameters.AppendElement( michael@0: BluetoothNamedValue(NS_LITERAL_STRING("address"), signalPath)); michael@0: parameters.AppendElement( michael@0: BluetoothNamedValue(NS_LITERAL_STRING("status"), status)); michael@0: signal.path() = NS_LITERAL_STRING(KEY_ADAPTER); michael@0: signal.value() = parameters; michael@0: NS_DispatchToMainThread(new DistributeBluetoothSignalTask(signal)); michael@0: } else if (property.name().EqualsLiteral("Connected")) { michael@0: MonitorAutoLock lock(*sStopBluetoothMonitor); michael@0: michael@0: if (property.value().get_bool()) { michael@0: ++sConnectedDeviceCount; michael@0: } else { michael@0: MOZ_ASSERT(sConnectedDeviceCount > 0); michael@0: if (--sConnectedDeviceCount == 0) { michael@0: lock.Notify(); michael@0: } michael@0: } michael@0: } michael@0: } else if (dbus_message_is_signal(aMsg, DBUS_MANAGER_IFACE, "AdapterAdded")) { michael@0: const char* str; michael@0: if (!dbus_message_get_args(aMsg, &err, michael@0: DBUS_TYPE_OBJECT_PATH, &str, michael@0: DBUS_TYPE_INVALID)) { michael@0: LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, aMsg); michael@0: errorStr.AssignLiteral("Cannot parse manager path!"); michael@0: } else { michael@0: v = NS_ConvertUTF8toUTF16(str); michael@0: sAdapterPath = v.get_nsString(); michael@0: NS_DispatchToMainThread(new TryFiringAdapterAddedRunnable(true)); michael@0: NS_DispatchToMainThread(new PrepareAdapterRunnable()); michael@0: michael@0: /** michael@0: * The adapter name isn't ready for the time being. Wait for the upcoming michael@0: * signal PropertyChanged of adapter name, and then propagate signal michael@0: * AdapterAdded to BluetoothManager. michael@0: */ michael@0: return DBUS_HANDLER_RESULT_HANDLED; michael@0: } michael@0: } else if (dbus_message_is_signal(aMsg, DBUS_MANAGER_IFACE, michael@0: "PropertyChanged")) { michael@0: ParsePropertyChange(aMsg, michael@0: v, michael@0: errorStr, michael@0: sManagerProperties, michael@0: ArrayLength(sManagerProperties)); michael@0: } else if (dbus_message_is_signal(aMsg, DBUS_SINK_IFACE, michael@0: "PropertyChanged")) { michael@0: ParsePropertyChange(aMsg, michael@0: v, michael@0: errorStr, michael@0: sSinkProperties, michael@0: ArrayLength(sSinkProperties)); michael@0: } else if (dbus_message_is_signal(aMsg, DBUS_CTL_IFACE, "GetPlayStatus")) { michael@0: NS_DispatchToMainThread(new RequestPlayStatusTask()); michael@0: return DBUS_HANDLER_RESULT_HANDLED; michael@0: } else if (dbus_message_is_signal(aMsg, DBUS_CTL_IFACE, "PropertyChanged")) { michael@0: ParsePropertyChange(aMsg, michael@0: v, michael@0: errorStr, michael@0: sControlProperties, michael@0: ArrayLength(sControlProperties)); michael@0: } else if (dbus_message_is_signal(aMsg, DBUS_INPUT_IFACE, michael@0: "PropertyChanged")) { michael@0: ParsePropertyChange(aMsg, michael@0: v, michael@0: errorStr, michael@0: sInputProperties, michael@0: ArrayLength(sInputProperties)); michael@0: } else { michael@0: errorStr = NS_ConvertUTF8toUTF16(dbus_message_get_member(aMsg)); michael@0: errorStr.AppendLiteral(" Signal not handled!"); michael@0: } michael@0: michael@0: if (!errorStr.IsEmpty()) { michael@0: BT_WARNING(NS_ConvertUTF16toUTF8(errorStr).get()); michael@0: return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; michael@0: } michael@0: michael@0: BluetoothSignal signal(signalName, signalPath, v); michael@0: nsRefPtr task; michael@0: if (signalInterface.EqualsLiteral(DBUS_SINK_IFACE)) { michael@0: task = new SinkPropertyChangedHandler(signal); michael@0: } else if (signalInterface.EqualsLiteral(DBUS_CTL_IFACE)) { michael@0: task = new ControlPropertyChangedHandler(signal); michael@0: } else if (signalInterface.EqualsLiteral(DBUS_INPUT_IFACE)) { michael@0: task = new InputPropertyChangedHandler(signal); michael@0: } else { michael@0: task = new DistributeBluetoothSignalTask(signal); michael@0: } michael@0: michael@0: NS_DispatchToMainThread(task); michael@0: michael@0: return DBUS_HANDLER_RESULT_HANDLED; michael@0: } michael@0: michael@0: static void michael@0: OnDefaultAdapterReply(DBusMessage* aReply, void* aData) michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); // I/O thread michael@0: michael@0: if (!aReply || dbus_message_is_error(aReply, DBUS_ERROR_TIMEOUT)) { michael@0: return; michael@0: } michael@0: michael@0: DBusError err; michael@0: dbus_error_init(&err); michael@0: michael@0: BluetoothValue v; michael@0: nsAutoString errorString; michael@0: michael@0: UnpackObjectPathMessage(aReply, &err, v, errorString); michael@0: michael@0: if (!errorString.IsEmpty()) { michael@0: return; michael@0: } michael@0: michael@0: sAdapterPath = v.get_nsString(); michael@0: michael@0: nsRefPtr b = new PrepareAdapterRunnable(); michael@0: if (NS_FAILED(NS_DispatchToMainThread(b))) { michael@0: BT_WARNING("Failed to dispatch to main thread!"); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: BluetoothDBusService::IsReady() michael@0: { michael@0: if (!IsEnabled() || !sDBusConnection || IsToggling()) { michael@0: BT_WARNING("Bluetooth service is not ready yet!"); michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: class StartDBusConnectionTask : public Task michael@0: { michael@0: public: michael@0: StartDBusConnectionTask(RawDBusConnection* aConnection) michael@0: : mConnection(aConnection) michael@0: { michael@0: MOZ_ASSERT(mConnection); michael@0: } michael@0: michael@0: void Run() michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); // I/O thread michael@0: michael@0: if (sDBusConnection) { michael@0: BT_WARNING("DBus connection has already been established."); michael@0: nsRefPtr runnable = new BluetoothService::ToggleBtAck(true); michael@0: if (NS_FAILED(NS_DispatchToMainThread(runnable))) { michael@0: BT_WARNING("Failed to dispatch to main thread!"); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: // Add a filter for all incoming messages_base michael@0: if (!dbus_connection_add_filter(mConnection->GetConnection(), michael@0: EventFilter, nullptr, nullptr)) { michael@0: BT_WARNING("Cannot create DBus Event Filter for DBus Thread!"); michael@0: nsRefPtr runnable = new BluetoothService::ToggleBtAck(false); michael@0: if (NS_FAILED(NS_DispatchToMainThread(runnable))) { michael@0: BT_WARNING("Failed to dispatch to main thread!"); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: mConnection->Watch(); michael@0: michael@0: if (!sPairingReqTable) { michael@0: sPairingReqTable = new nsDataHashtable; michael@0: } michael@0: michael@0: sDBusConnection = mConnection.forget(); michael@0: michael@0: nsRefPtr runnable = michael@0: new BluetoothService::ToggleBtAck(true); michael@0: if (NS_FAILED(NS_DispatchToMainThread(runnable))) { michael@0: BT_WARNING("Failed to dispatch to main thread!"); michael@0: return; michael@0: } michael@0: michael@0: /* Normally we'll receive the signal 'AdapterAdded' with the adapter object michael@0: * path from the DBus daemon during start up. So, there's no need to query michael@0: * the object path of default adapter here. However, if we restart from a michael@0: * crash, the default adapter might already be available, so we ask the daemon michael@0: * explicitly here. michael@0: */ michael@0: if (sAdapterPath.IsEmpty()) { michael@0: bool success = sDBusConnection->SendWithReply(OnDefaultAdapterReply, nullptr, michael@0: 1000, BLUEZ_DBUS_BASE_IFC, "/", michael@0: DBUS_MANAGER_IFACE, michael@0: "DefaultAdapter", michael@0: DBUS_TYPE_INVALID); michael@0: if (!success) { michael@0: BT_WARNING("Failed to query default adapter!"); michael@0: } michael@0: } michael@0: } michael@0: michael@0: private: michael@0: nsAutoPtr mConnection; michael@0: }; michael@0: michael@0: class StartBluetoothRunnable MOZ_FINAL : public nsRunnable michael@0: { michael@0: public: michael@0: NS_IMETHOD Run() michael@0: { michael@0: // This could block. It should never be run on the main thread. michael@0: MOZ_ASSERT(!NS_IsMainThread()); // BT thread michael@0: michael@0: #ifdef MOZ_WIDGET_GONK michael@0: if (!sBluedroid.Enable()) { michael@0: BT_WARNING("Bluetooth not available."); michael@0: nsRefPtr runnable = new BluetoothService::ToggleBtAck(false); michael@0: if (NS_FAILED(NS_DispatchToMainThread(runnable))) { michael@0: BT_WARNING("Failed to dispatch to main thread!"); michael@0: } michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: #endif michael@0: michael@0: RawDBusConnection* connection = new RawDBusConnection(); michael@0: nsresult rv = connection->EstablishDBusConnection(); michael@0: if (NS_FAILED(rv)) { michael@0: BT_WARNING("Failed to establish connection to BlueZ daemon"); michael@0: nsRefPtr runnable = new BluetoothService::ToggleBtAck(false); michael@0: if (NS_FAILED(NS_DispatchToMainThread(runnable))) { michael@0: BT_WARNING("Failed to dispatch to main thread!"); michael@0: } michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: DBusError err; michael@0: dbus_error_init(&err); michael@0: michael@0: // Set which messages will be processed by this dbus connection. michael@0: // Since we are maintaining a single thread for all the DBus bluez michael@0: // signals we want, register all of them in this thread at startup. michael@0: // The event handler will sort the destinations out as needed. The michael@0: // call to dbus_bus_add_match has to run on the BT thread because michael@0: // it can block. michael@0: for (uint32_t i = 0; i < ArrayLength(sBluetoothDBusSignals); ++i) { michael@0: dbus_bus_add_match(connection->GetConnection(), michael@0: sBluetoothDBusSignals[i], michael@0: &err); michael@0: if (dbus_error_is_set(&err)) { michael@0: LOG_AND_FREE_DBUS_ERROR(&err); michael@0: } michael@0: } michael@0: michael@0: Task* task = new StartDBusConnectionTask(connection); michael@0: DispatchToDBusThread(task); michael@0: michael@0: return NS_OK; michael@0: } michael@0: }; michael@0: michael@0: nsresult michael@0: BluetoothDBusService::StartInternal() michael@0: { michael@0: nsRefPtr runnable = new StartBluetoothRunnable(); michael@0: nsresult rv = DispatchToBtThread(runnable); michael@0: if (NS_FAILED(rv)) { michael@0: BT_WARNING("Failed to dispatch to BT thread!"); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: class DisableBluetoothRunnable MOZ_FINAL : public nsRunnable michael@0: { michael@0: public: michael@0: NS_IMETHOD Run() michael@0: { michael@0: if (NS_IsMainThread()) { michael@0: // Clear |sControllerArray| here while we're on the main thread michael@0: sControllerArray.Clear(); michael@0: // Forward this runnable to BT thread michael@0: return DispatchToBtThread(this); michael@0: } michael@0: michael@0: #ifdef MOZ_WIDGET_GONK michael@0: MOZ_ASSERT(sBluedroid.IsEnabled()); michael@0: // Disable() return true on success, so we need to invert it michael@0: bool isEnabled = !sBluedroid.Disable(); michael@0: #else michael@0: bool isEnabled = false; michael@0: #endif michael@0: michael@0: nsRefPtr runnable = michael@0: new BluetoothService::ToggleBtAck(isEnabled); michael@0: nsresult rv = NS_DispatchToMainThread(runnable); michael@0: if (NS_FAILED(rv)) { michael@0: BT_WARNING("Failed to dispatch to main thread!"); michael@0: } michael@0: return rv; michael@0: } michael@0: }; michael@0: michael@0: class DeleteDBusConnectionTask MOZ_FINAL : public Task michael@0: { michael@0: public: michael@0: DeleteDBusConnectionTask() michael@0: { } michael@0: michael@0: void Run() michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); // I/O thread michael@0: michael@0: if (!sDBusConnection) { michael@0: BT_WARNING("DBus connection has not been established."); michael@0: nsRefPtr runnable = new BluetoothService::ToggleBtAck(false); michael@0: if (NS_FAILED(NS_DispatchToMainThread(runnable))) { michael@0: BT_WARNING("Failed to dispatch to main thread!"); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: for (uint32_t i = 0; i < ArrayLength(sBluetoothDBusSignals); ++i) { michael@0: dbus_bus_remove_match(sDBusConnection->GetConnection(), michael@0: sBluetoothDBusSignals[i], NULL); michael@0: } michael@0: michael@0: dbus_connection_remove_filter(sDBusConnection->GetConnection(), michael@0: EventFilter, nullptr); michael@0: michael@0: if (!dbus_connection_unregister_object_path(sDBusConnection->GetConnection(), michael@0: KEY_LOCAL_AGENT)) { michael@0: BT_WARNING("%s: Can't unregister object path %s for agent!", michael@0: __FUNCTION__, KEY_LOCAL_AGENT); michael@0: } michael@0: michael@0: if (!dbus_connection_unregister_object_path(sDBusConnection->GetConnection(), michael@0: KEY_REMOTE_AGENT)) { michael@0: BT_WARNING("%s: Can't unregister object path %s for agent!", michael@0: __FUNCTION__, KEY_REMOTE_AGENT); michael@0: } michael@0: michael@0: // unref stored DBusMessages before clearing the hashtable michael@0: sPairingReqTable->EnumerateRead(UnrefDBusMessage, nullptr); michael@0: sPairingReqTable->Clear(); michael@0: michael@0: sIsPairing = 0; michael@0: sConnectedDeviceCount = 0; michael@0: michael@0: // This command closes the DBus connection and all its instances michael@0: // of DBusWatch will be removed and free'd. michael@0: sDBusConnection = nullptr; michael@0: michael@0: // We can only dispatch to the BT thread if we're on the main michael@0: // thread. Thus we dispatch our runnable to the main thread michael@0: // from where it will forward itself to the BT thread. michael@0: nsRefPtr runnable = new DisableBluetoothRunnable(); michael@0: if (NS_FAILED(NS_DispatchToMainThread(runnable))) { michael@0: BT_WARNING("Failed to dispatch to BT thread!"); michael@0: } michael@0: } michael@0: michael@0: private: michael@0: static PLDHashOperator michael@0: UnrefDBusMessage(const nsAString& key, DBusMessage* value, void* arg) michael@0: { michael@0: dbus_message_unref(value); michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: }; michael@0: michael@0: class StopBluetoothRunnable MOZ_FINAL : public nsRunnable michael@0: { michael@0: public: michael@0: NS_IMETHOD Run() michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); // BT thread michael@0: michael@0: // This could block. It should never be run on the main thread. michael@0: MonitorAutoLock lock(*sStopBluetoothMonitor); michael@0: if (sConnectedDeviceCount > 0) { michael@0: lock.Wait(PR_SecondsToInterval(TIMEOUT_FORCE_TO_DISABLE_BT)); michael@0: } michael@0: michael@0: DispatchToDBusThread(new DeleteDBusConnectionTask()); michael@0: michael@0: return NS_OK; michael@0: } michael@0: }; michael@0: michael@0: nsresult michael@0: BluetoothDBusService::StopInternal() michael@0: { michael@0: nsRefPtr runnable = new StopBluetoothRunnable(); michael@0: nsresult rv = DispatchToBtThread(runnable); michael@0: if (NS_FAILED(rv)) { michael@0: BT_WARNING("Failed to dispatch to BT thread!"); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: class DefaultAdapterPathReplyHandler : public DBusReplyHandler michael@0: { michael@0: public: michael@0: DefaultAdapterPathReplyHandler(BluetoothReplyRunnable* aRunnable) michael@0: : mRunnable(aRunnable) michael@0: { michael@0: MOZ_ASSERT(mRunnable); michael@0: } michael@0: michael@0: void Handle(DBusMessage* aReply) MOZ_OVERRIDE michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); // I/O thread michael@0: michael@0: if (!aReply || (dbus_message_get_type(aReply) == DBUS_MESSAGE_TYPE_ERROR)) { michael@0: const char* errStr = "Timeout in DefaultAdapterPathReplyHandler"; michael@0: if (aReply) { michael@0: errStr = dbus_message_get_error_name(aReply); michael@0: if (!errStr) { michael@0: errStr = "Bluetooth DBus Error"; michael@0: } michael@0: } michael@0: DispatchBluetoothReply(mRunnable, BluetoothValue(), michael@0: NS_ConvertUTF8toUTF16(errStr)); michael@0: return; michael@0: } michael@0: michael@0: bool success; michael@0: nsAutoString replyError; michael@0: michael@0: if (mAdapterPath.IsEmpty()) { michael@0: success = HandleDefaultAdapterPathReply(aReply, replyError); michael@0: } else { michael@0: success = HandleGetPropertiesReply(aReply, replyError); michael@0: } michael@0: michael@0: if (!success) { michael@0: DispatchBluetoothReply(mRunnable, BluetoothValue(), replyError); michael@0: } michael@0: } michael@0: michael@0: protected: michael@0: bool HandleDefaultAdapterPathReply(DBusMessage* aReply, michael@0: nsAString& aReplyError) michael@0: { michael@0: BluetoothValue value; michael@0: DBusError error; michael@0: dbus_error_init(&error); michael@0: michael@0: MOZ_ASSERT(!NS_IsMainThread()); // I/O thread michael@0: MOZ_ASSERT(sDBusConnection); michael@0: michael@0: UnpackObjectPathMessage(aReply, &error, value, aReplyError); michael@0: michael@0: if (!aReplyError.IsEmpty()) { michael@0: return false; michael@0: } michael@0: michael@0: mAdapterPath = value.get_nsString(); michael@0: michael@0: // Acquire another reference to this reply handler michael@0: nsRefPtr handler = this; michael@0: michael@0: bool success = sDBusConnection->SendWithReply( michael@0: DefaultAdapterPathReplyHandler::Callback, handler.get(), 1000, michael@0: BLUEZ_DBUS_BASE_IFC, michael@0: NS_ConvertUTF16toUTF8(mAdapterPath).get(), michael@0: DBUS_ADAPTER_IFACE, "GetProperties", DBUS_TYPE_INVALID); michael@0: michael@0: if (!success) { michael@0: aReplyError = NS_LITERAL_STRING("SendWithReply failed"); michael@0: return false; michael@0: } michael@0: michael@0: unused << handler.forget(); // picked up by callback handler michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool HandleGetPropertiesReply(DBusMessage* aReply, michael@0: nsAutoString& aReplyError) michael@0: { michael@0: BluetoothValue value; michael@0: DBusError error; michael@0: dbus_error_init(&error); michael@0: michael@0: MOZ_ASSERT(!NS_IsMainThread()); // I/O thread michael@0: michael@0: bool success = UnpackPropertiesMessage(aReply, &error, value, michael@0: DBUS_ADAPTER_IFACE); michael@0: if (!success) { michael@0: aReplyError = NS_ConvertUTF8toUTF16(error.message); michael@0: return false; michael@0: } michael@0: michael@0: // We have to manually attach the path to the rest of the elements michael@0: value.get_ArrayOfBluetoothNamedValue().AppendElement( michael@0: BluetoothNamedValue(NS_LITERAL_STRING("Path"), mAdapterPath)); michael@0: michael@0: // Dispatch result michael@0: DispatchBluetoothReply(mRunnable, value, aReplyError); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: private: michael@0: nsRefPtr mRunnable; michael@0: nsString mAdapterPath; michael@0: }; michael@0: michael@0: class DefaultAdapterTask : public Task michael@0: { michael@0: public: michael@0: DefaultAdapterTask(BluetoothReplyRunnable* aRunnable) michael@0: : mRunnable(aRunnable) michael@0: { michael@0: MOZ_ASSERT(mRunnable); michael@0: } michael@0: michael@0: void Run() MOZ_OVERRIDE michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); // I/O thread michael@0: MOZ_ASSERT(sDBusConnection); michael@0: michael@0: nsRefPtr handler = michael@0: new DefaultAdapterPathReplyHandler(mRunnable); michael@0: michael@0: bool success = sDBusConnection->SendWithReply( michael@0: DefaultAdapterPathReplyHandler::Callback, michael@0: handler.get(), 1000, BLUEZ_DBUS_BASE_IFC, michael@0: "/", DBUS_MANAGER_IFACE, "DefaultAdapter", michael@0: DBUS_TYPE_INVALID); michael@0: NS_ENSURE_TRUE_VOID(success); michael@0: michael@0: unused << handler.forget(); // picked up by callback handler michael@0: } michael@0: michael@0: private: michael@0: nsRefPtr mRunnable; michael@0: }; michael@0: michael@0: nsresult michael@0: BluetoothDBusService::GetDefaultAdapterPathInternal( michael@0: BluetoothReplyRunnable* aRunnable) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: if (!IsReady()) { michael@0: NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!"); michael@0: DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr); michael@0: return NS_OK; michael@0: } michael@0: michael@0: Task* task = new DefaultAdapterTask(aRunnable); michael@0: DispatchToDBusThread(task); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: static void michael@0: OnSendDiscoveryMessageReply(DBusMessage *aReply, void *aData) michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); // I/O thread michael@0: michael@0: nsAutoString errorStr; michael@0: michael@0: if (!aReply) { michael@0: errorStr.AssignLiteral("SendDiscovery failed"); michael@0: } michael@0: michael@0: nsRefPtr runnable = michael@0: dont_AddRef(static_cast(aData)); michael@0: michael@0: DispatchBluetoothReply(runnable.get(), BluetoothValue(true), errorStr); michael@0: } michael@0: michael@0: class SendDiscoveryMessageTask : public Task michael@0: { michael@0: public: michael@0: SendDiscoveryMessageTask(const char* aMessageName, michael@0: BluetoothReplyRunnable* aRunnable) michael@0: : mMessageName(aMessageName) michael@0: , mRunnable(aRunnable) michael@0: { michael@0: MOZ_ASSERT(!mMessageName.IsEmpty()); michael@0: MOZ_ASSERT(mRunnable); michael@0: } michael@0: michael@0: void Run() MOZ_OVERRIDE michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); // I/O thread michael@0: MOZ_ASSERT(sDBusConnection); michael@0: MOZ_ASSERT(!sAdapterPath.IsEmpty()); michael@0: michael@0: bool success = sDBusConnection->SendWithReply( michael@0: OnSendDiscoveryMessageReply, michael@0: static_cast(mRunnable.get()), -1, michael@0: BLUEZ_DBUS_BASE_IFC, michael@0: NS_ConvertUTF16toUTF8(sAdapterPath).get(), michael@0: DBUS_ADAPTER_IFACE, mMessageName.get(), michael@0: DBUS_TYPE_INVALID); michael@0: NS_ENSURE_TRUE_VOID(success); michael@0: michael@0: unused << mRunnable.forget(); // picked up by callback handler michael@0: } michael@0: michael@0: private: michael@0: const nsCString mMessageName; michael@0: nsRefPtr mRunnable; michael@0: }; michael@0: michael@0: nsresult michael@0: BluetoothDBusService::SendDiscoveryMessage(const char* aMessageName, michael@0: BluetoothReplyRunnable* aRunnable) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_ASSERT(!sAdapterPath.IsEmpty()); michael@0: michael@0: if (!IsReady()) { michael@0: NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!"); michael@0: DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr); michael@0: return NS_OK; michael@0: } michael@0: michael@0: Task* task = new SendDiscoveryMessageTask(aMessageName, aRunnable); michael@0: DispatchToDBusThread(task); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: BluetoothDBusService::SendInputMessage(const nsAString& aDeviceAddress, michael@0: const nsAString& aMessage) michael@0: { michael@0: DBusReplyCallback callback; michael@0: if (aMessage.EqualsLiteral("Connect")) { michael@0: callback = InputConnectCallback; michael@0: } else if (aMessage.EqualsLiteral("Disconnect")) { michael@0: callback = InputDisconnectCallback; michael@0: } else { michael@0: MOZ_ASSERT(false); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: MOZ_ASSERT(!sAdapterPath.IsEmpty()); michael@0: nsString objectPath = GetObjectPathFromAddress(sAdapterPath, aDeviceAddress); michael@0: return SendAsyncDBusMessage(objectPath, DBUS_INPUT_IFACE, aMessage, callback); michael@0: } michael@0: michael@0: class SendAsyncDBusMessageTask : public Task michael@0: { michael@0: public: michael@0: SendAsyncDBusMessageTask(DBusReplyCallback aCallback, michael@0: BluetoothServiceClass* aServiceClass, michael@0: const nsACString& aObjectPath, michael@0: const char* aInterface, michael@0: const nsACString& aMessage) michael@0: : mCallback(aCallback) michael@0: , mServiceClass(aServiceClass) michael@0: , mObjectPath(aObjectPath) michael@0: , mInterface(aInterface) michael@0: , mMessage(aMessage) michael@0: { michael@0: MOZ_ASSERT(mServiceClass); michael@0: MOZ_ASSERT(!mObjectPath.IsEmpty()); michael@0: MOZ_ASSERT(!mInterface.IsEmpty()); michael@0: MOZ_ASSERT(!mMessage.IsEmpty()); michael@0: } michael@0: michael@0: void Run() MOZ_OVERRIDE michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); // I/O thread michael@0: MOZ_ASSERT(sDBusConnection); michael@0: michael@0: bool success = sDBusConnection->SendWithReply( michael@0: mCallback, static_cast(mServiceClass), -1, michael@0: BLUEZ_DBUS_BASE_IFC, mObjectPath.get(), mInterface.get(), michael@0: mMessage.get(), DBUS_TYPE_INVALID); michael@0: NS_ENSURE_TRUE_VOID(success); michael@0: michael@0: mServiceClass.forget(); michael@0: } michael@0: michael@0: private: michael@0: DBusReplyCallback mCallback; michael@0: nsAutoPtr mServiceClass; michael@0: const nsCString mObjectPath; michael@0: const nsCString mInterface; michael@0: const nsCString mMessage; michael@0: }; michael@0: michael@0: nsresult michael@0: BluetoothDBusService::SendAsyncDBusMessage(const nsAString& aObjectPath, michael@0: const char* aInterface, michael@0: const nsAString& aMessage, michael@0: DBusReplyCallback aCallback) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_ASSERT(IsEnabled()); michael@0: MOZ_ASSERT(aCallback); michael@0: MOZ_ASSERT(!aObjectPath.IsEmpty()); michael@0: MOZ_ASSERT(aInterface); michael@0: michael@0: nsAutoPtr serviceClass(new BluetoothServiceClass()); michael@0: if (!strcmp(aInterface, DBUS_SINK_IFACE)) { michael@0: *serviceClass = BluetoothServiceClass::A2DP; michael@0: } else if (!strcmp(aInterface, DBUS_INPUT_IFACE)) { michael@0: *serviceClass = BluetoothServiceClass::HID; michael@0: } else { michael@0: MOZ_ASSERT(false); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: Task* task = new SendAsyncDBusMessageTask(aCallback, michael@0: serviceClass.forget(), michael@0: NS_ConvertUTF16toUTF8(aObjectPath), michael@0: aInterface, michael@0: NS_ConvertUTF16toUTF8(aMessage)); michael@0: DispatchToDBusThread(task); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: BluetoothDBusService::SendSinkMessage(const nsAString& aDeviceAddress, michael@0: const nsAString& aMessage) michael@0: { michael@0: DBusReplyCallback callback; michael@0: if (aMessage.EqualsLiteral("Connect")) { michael@0: callback = SinkConnectCallback; michael@0: } else if (aMessage.EqualsLiteral("Disconnect")) { michael@0: callback = SinkDisconnectCallback; michael@0: } else { michael@0: MOZ_ASSERT(false); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: MOZ_ASSERT(!sAdapterPath.IsEmpty()); michael@0: nsString objectPath = GetObjectPathFromAddress(sAdapterPath, aDeviceAddress); michael@0: return SendAsyncDBusMessage(objectPath, DBUS_SINK_IFACE, aMessage, callback); michael@0: } michael@0: michael@0: nsresult michael@0: BluetoothDBusService::StopDiscoveryInternal(BluetoothReplyRunnable* aRunnable) michael@0: { michael@0: return SendDiscoveryMessage("StopDiscovery", aRunnable); michael@0: } michael@0: michael@0: nsresult michael@0: BluetoothDBusService::StartDiscoveryInternal(BluetoothReplyRunnable* aRunnable) michael@0: { michael@0: return SendDiscoveryMessage("StartDiscovery", aRunnable); michael@0: } michael@0: michael@0: class BluetoothArrayOfDevicePropertiesReplyHandler : public DBusReplyHandler michael@0: { michael@0: public: michael@0: BluetoothArrayOfDevicePropertiesReplyHandler( michael@0: const nsTArray& aDeviceAddresses, michael@0: const FilterFunc aFilterFunc, BluetoothReplyRunnable* aRunnable) michael@0: : mDeviceAddresses(aDeviceAddresses) michael@0: , mProcessedDeviceAddresses(0) michael@0: , mFilterFunc(aFilterFunc) michael@0: , mRunnable(aRunnable) michael@0: , mValues(InfallibleTArray()) michael@0: { michael@0: MOZ_ASSERT(mRunnable); michael@0: } michael@0: michael@0: void Handle(DBusMessage* aReply) MOZ_OVERRIDE michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); // I/O thread michael@0: MOZ_ASSERT(!sAdapterPath.IsEmpty()); michael@0: MOZ_ASSERT(!mObjectPath.IsEmpty()); michael@0: MOZ_ASSERT(mProcessedDeviceAddresses < mDeviceAddresses.Length()); michael@0: michael@0: const nsTArray::index_type i = mProcessedDeviceAddresses++; michael@0: michael@0: if (!aReply || michael@0: (dbus_message_get_type(aReply) == DBUS_MESSAGE_TYPE_ERROR)) { michael@0: BT_WARNING("Invalid DBus message"); michael@0: ProcessRemainingDeviceAddresses(); michael@0: return; michael@0: } michael@0: michael@0: // Get device properties from result of GetProperties michael@0: michael@0: DBusError err; michael@0: dbus_error_init(&err); michael@0: michael@0: BluetoothValue deviceProperties; michael@0: michael@0: bool success = UnpackPropertiesMessage(aReply, &err, deviceProperties, michael@0: DBUS_DEVICE_IFACE); michael@0: if (!success) { michael@0: BT_WARNING("Failed to get device properties"); michael@0: ProcessRemainingDeviceAddresses(); michael@0: return; michael@0: } michael@0: michael@0: InfallibleTArray& devicePropertiesArray = michael@0: deviceProperties.get_ArrayOfBluetoothNamedValue(); michael@0: michael@0: // We have to manually attach the path to the rest of the elements michael@0: devicePropertiesArray.AppendElement( michael@0: BluetoothNamedValue(NS_LITERAL_STRING("Path"), mObjectPath)); michael@0: michael@0: // It is possible that property Icon missed due to CoD of major michael@0: // class is TOY but service class is "Audio", we need to assign michael@0: // Icon as audio-card. This is for PTS test TC_AG_COD_BV_02_I. michael@0: // As HFP specification defined that michael@0: // service class is "Audio" can be considered as HFP AG. michael@0: if (!ContainsIcon(devicePropertiesArray)) { michael@0: for (uint32_t j = 0; j < devicePropertiesArray.Length(); ++j) { michael@0: BluetoothNamedValue& deviceProperty = devicePropertiesArray[j]; michael@0: if (deviceProperty.name().EqualsLiteral("Class")) { michael@0: if (HasAudioService(deviceProperty.value().get_uint32_t())) { michael@0: devicePropertiesArray.AppendElement( michael@0: BluetoothNamedValue(NS_LITERAL_STRING("Icon"), michael@0: NS_LITERAL_STRING("audio-card"))); michael@0: } michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (mFilterFunc(deviceProperties)) { michael@0: mValues.get_ArrayOfBluetoothNamedValue().AppendElement( michael@0: BluetoothNamedValue(mDeviceAddresses[i], deviceProperties)); michael@0: } michael@0: michael@0: ProcessRemainingDeviceAddresses(); michael@0: } michael@0: michael@0: void ProcessRemainingDeviceAddresses() michael@0: { michael@0: if (mProcessedDeviceAddresses < mDeviceAddresses.Length()) { michael@0: if (!SendNextGetProperties()) { michael@0: DispatchBluetoothReply(mRunnable, BluetoothValue(), michael@0: NS_LITERAL_STRING( michael@0: "SendNextGetProperties failed")); michael@0: } michael@0: } else { michael@0: // Send resulting device properties michael@0: DispatchBluetoothReply(mRunnable, mValues, EmptyString()); michael@0: } michael@0: } michael@0: michael@0: protected: michael@0: bool SendNextGetProperties() michael@0: { michael@0: MOZ_ASSERT(mProcessedDeviceAddresses < mDeviceAddresses.Length()); michael@0: MOZ_ASSERT(!sAdapterPath.IsEmpty()); michael@0: MOZ_ASSERT(sDBusConnection); michael@0: michael@0: // cache object path for reply michael@0: mObjectPath = GetObjectPathFromAddress(sAdapterPath, michael@0: mDeviceAddresses[mProcessedDeviceAddresses]); michael@0: michael@0: nsRefPtr handler = this; michael@0: michael@0: bool success = sDBusConnection->SendWithReply( michael@0: BluetoothArrayOfDevicePropertiesReplyHandler::Callback, michael@0: handler.get(), 1000, BLUEZ_DBUS_BASE_IFC, michael@0: NS_ConvertUTF16toUTF8(mObjectPath).get(), michael@0: DBUS_DEVICE_IFACE, "GetProperties", michael@0: DBUS_TYPE_INVALID); michael@0: michael@0: NS_ENSURE_TRUE(success, false); michael@0: michael@0: unused << handler.forget(); // picked up by callback handler michael@0: michael@0: return true; michael@0: } michael@0: michael@0: private: michael@0: nsString mObjectPath; michael@0: const nsTArray mDeviceAddresses; michael@0: nsTArray::size_type mProcessedDeviceAddresses; michael@0: const FilterFunc mFilterFunc; michael@0: nsRefPtr mRunnable; michael@0: BluetoothValue mValues; michael@0: }; michael@0: michael@0: class ProcessRemainingDeviceAddressesTask : public Task michael@0: { michael@0: public: michael@0: ProcessRemainingDeviceAddressesTask( michael@0: BluetoothArrayOfDevicePropertiesReplyHandler* aHandler, michael@0: BluetoothReplyRunnable* aRunnable) michael@0: : mHandler(aHandler) michael@0: , mRunnable(aRunnable) michael@0: { michael@0: MOZ_ASSERT(mHandler); michael@0: MOZ_ASSERT(mRunnable); michael@0: } michael@0: michael@0: void Run() MOZ_OVERRIDE michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); // I/O thread michael@0: michael@0: mHandler->ProcessRemainingDeviceAddresses(); michael@0: } michael@0: michael@0: private: michael@0: nsRefPtr mHandler; michael@0: nsRefPtr mRunnable; michael@0: }; michael@0: michael@0: nsresult michael@0: BluetoothDBusService::GetConnectedDevicePropertiesInternal(uint16_t aServiceUuid, michael@0: BluetoothReplyRunnable* aRunnable) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: nsAutoString errorStr; michael@0: BluetoothValue values = InfallibleTArray(); michael@0: if (!IsReady()) { michael@0: NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!"); michael@0: DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsTArray deviceAddresses; michael@0: BluetoothProfileManagerBase* profile = michael@0: BluetoothUuidHelper::GetBluetoothProfileManager(aServiceUuid); michael@0: if (!profile) { michael@0: DispatchBluetoothReply(aRunnable, values, michael@0: NS_LITERAL_STRING(ERR_UNKNOWN_PROFILE)); michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (profile->IsConnected()) { michael@0: nsString address; michael@0: profile->GetAddress(address); michael@0: deviceAddresses.AppendElement(address); michael@0: } michael@0: michael@0: BluetoothArrayOfDevicePropertiesReplyHandler* handler = michael@0: new BluetoothArrayOfDevicePropertiesReplyHandler(deviceAddresses, michael@0: GetConnectedDevicesFilter, michael@0: aRunnable); michael@0: Task* task = new ProcessRemainingDeviceAddressesTask(handler, aRunnable); michael@0: DispatchToDBusThread(task); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: BluetoothDBusService::GetPairedDevicePropertiesInternal( michael@0: const nsTArray& aDeviceAddresses, michael@0: BluetoothReplyRunnable* aRunnable) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: if (!IsReady()) { michael@0: NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!"); michael@0: DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr); michael@0: return NS_OK; michael@0: } michael@0: michael@0: BluetoothArrayOfDevicePropertiesReplyHandler* handler = michael@0: new BluetoothArrayOfDevicePropertiesReplyHandler(aDeviceAddresses, michael@0: GetPairedDevicesFilter, michael@0: aRunnable); michael@0: Task* task = new ProcessRemainingDeviceAddressesTask(handler, aRunnable); michael@0: DispatchToDBusThread(task); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: class SetPropertyTask : public Task michael@0: { michael@0: public: michael@0: SetPropertyTask(BluetoothObjectType aType, michael@0: const nsACString& aName, michael@0: BluetoothReplyRunnable* aRunnable) michael@0: : mType(aType) michael@0: , mName(aName) michael@0: , mRunnable(aRunnable) michael@0: { michael@0: MOZ_ASSERT(mRunnable); michael@0: } michael@0: michael@0: void Send(unsigned int aType, const void* aValue) michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); // I/O thread michael@0: MOZ_ASSERT(sDBusConnection); michael@0: MOZ_ASSERT(!sAdapterPath.IsEmpty()); michael@0: michael@0: DBusMessage* msg = michael@0: dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, michael@0: NS_ConvertUTF16toUTF8(sAdapterPath).get(), michael@0: sBluetoothDBusIfaces[mType], michael@0: "SetProperty"); michael@0: if (!msg) { michael@0: BT_WARNING("Could not allocate D-Bus message object!"); michael@0: return; michael@0: } michael@0: michael@0: const char* name = mName.get(); michael@0: if (!dbus_message_append_args(msg, DBUS_TYPE_STRING, &name, michael@0: DBUS_TYPE_INVALID)) { michael@0: BT_WARNING("Couldn't append arguments to dbus message!"); michael@0: return; michael@0: } michael@0: michael@0: DBusMessageIter value_iter, iter; michael@0: dbus_message_iter_init_append(msg, &iter); michael@0: char var_type[2] = {(char)aType, '\0'}; michael@0: if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, michael@0: var_type, &value_iter) || michael@0: !dbus_message_iter_append_basic(&value_iter, aType, aValue) || michael@0: !dbus_message_iter_close_container(&iter, &value_iter)) { michael@0: BT_WARNING("Could not append argument to method call!"); michael@0: dbus_message_unref(msg); michael@0: return; michael@0: } michael@0: michael@0: // msg is unref'd as part of SendWithReply michael@0: bool success = sDBusConnection->SendWithReply( michael@0: GetVoidCallback, michael@0: static_cast(mRunnable), michael@0: 1000, msg); michael@0: NS_ENSURE_TRUE_VOID(success); michael@0: michael@0: unused << mRunnable.forget(); // picked up by callback handler michael@0: } michael@0: michael@0: private: michael@0: BluetoothObjectType mType; michael@0: const nsCString mName; michael@0: nsRefPtr mRunnable; michael@0: }; michael@0: michael@0: class SetUInt32PropertyTask : public SetPropertyTask michael@0: { michael@0: public: michael@0: SetUInt32PropertyTask(BluetoothObjectType aType, michael@0: const nsACString& aName, michael@0: uint32_t aValue, michael@0: BluetoothReplyRunnable* aRunnable) michael@0: : SetPropertyTask(aType, aName, aRunnable) michael@0: , mValue(aValue) michael@0: { } michael@0: michael@0: void Run() MOZ_OVERRIDE michael@0: { michael@0: Send(DBUS_TYPE_UINT32, &mValue); michael@0: } michael@0: michael@0: private: michael@0: dbus_uint32_t mValue; michael@0: }; michael@0: michael@0: class SetStringPropertyTask : public SetPropertyTask michael@0: { michael@0: public: michael@0: SetStringPropertyTask(BluetoothObjectType aType, michael@0: const nsACString& aName, michael@0: const nsACString& aValue, michael@0: BluetoothReplyRunnable* aRunnable) michael@0: : SetPropertyTask(aType, aName, aRunnable) michael@0: , mValue(aValue) michael@0: { } michael@0: michael@0: void Run() MOZ_OVERRIDE michael@0: { michael@0: const char* value = mValue.get(); michael@0: Send(DBUS_TYPE_STRING, &value); michael@0: } michael@0: michael@0: private: michael@0: const nsCString mValue; michael@0: }; michael@0: michael@0: class SetBooleanPropertyTask : public SetPropertyTask michael@0: { michael@0: public: michael@0: SetBooleanPropertyTask(BluetoothObjectType aType, michael@0: const nsACString& aName, michael@0: dbus_bool_t aValue, michael@0: BluetoothReplyRunnable* aRunnable) michael@0: : SetPropertyTask(aType, aName, aRunnable) michael@0: , mValue(aValue) michael@0: { michael@0: } michael@0: michael@0: void Run() MOZ_OVERRIDE michael@0: { michael@0: Send(DBUS_TYPE_BOOLEAN, &mValue); michael@0: } michael@0: michael@0: private: michael@0: dbus_bool_t mValue; michael@0: }; michael@0: michael@0: nsresult michael@0: BluetoothDBusService::SetProperty(BluetoothObjectType aType, michael@0: const BluetoothNamedValue& aValue, michael@0: BluetoothReplyRunnable* aRunnable) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: if (!IsReady()) { michael@0: NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!"); michael@0: DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr); michael@0: return NS_OK; michael@0: } michael@0: michael@0: Task* task; michael@0: michael@0: if (aValue.value().type() == BluetoothValue::Tuint32_t) { michael@0: task = new SetUInt32PropertyTask(aType, michael@0: NS_ConvertUTF16toUTF8(aValue.name()), michael@0: aValue.value().get_uint32_t(), aRunnable); michael@0: } else if (aValue.value().type() == BluetoothValue::TnsString) { michael@0: task = new SetStringPropertyTask(aType, michael@0: NS_ConvertUTF16toUTF8(aValue.name()), michael@0: NS_ConvertUTF16toUTF8(aValue.value().get_nsString()), aRunnable); michael@0: } else if (aValue.value().type() == BluetoothValue::Tbool) { michael@0: task = new SetBooleanPropertyTask(aType, michael@0: NS_ConvertUTF16toUTF8(aValue.name()), michael@0: aValue.value().get_bool(), aRunnable); michael@0: } else { michael@0: BT_WARNING("Property type not handled!"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: DispatchToDBusThread(task); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: class CreatePairedDeviceInternalTask : public Task michael@0: { michael@0: public: michael@0: CreatePairedDeviceInternalTask(const nsACString& aDeviceAddress, michael@0: int aTimeout, michael@0: BluetoothReplyRunnable* aRunnable) michael@0: : mDeviceAddress(aDeviceAddress) michael@0: , mTimeout(aTimeout) michael@0: , mRunnable(aRunnable) michael@0: { michael@0: MOZ_ASSERT(!mDeviceAddress.IsEmpty()); michael@0: MOZ_ASSERT(mRunnable); michael@0: } michael@0: michael@0: void Run() MOZ_OVERRIDE michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); // I/O thread michael@0: MOZ_ASSERT(sDBusConnection); michael@0: MOZ_ASSERT(!sAdapterPath.IsEmpty()); michael@0: michael@0: const char *deviceAddress = mDeviceAddress.get(); michael@0: const char *deviceAgentPath = KEY_REMOTE_AGENT; michael@0: const char *capabilities = B2G_AGENT_CAPABILITIES; michael@0: michael@0: // Then send CreatePairedDevice, it will register a temp device agent then michael@0: // unregister it after pairing process is over michael@0: bool success = sDBusConnection->SendWithReply( michael@0: GetObjectPathCallback, static_cast(mRunnable), mTimeout, michael@0: BLUEZ_DBUS_BASE_IFC, michael@0: NS_ConvertUTF16toUTF8(sAdapterPath).get(), michael@0: DBUS_ADAPTER_IFACE, michael@0: "CreatePairedDevice", michael@0: DBUS_TYPE_STRING, &deviceAddress, michael@0: DBUS_TYPE_OBJECT_PATH, &deviceAgentPath, michael@0: DBUS_TYPE_STRING, &capabilities, michael@0: DBUS_TYPE_INVALID); michael@0: NS_ENSURE_TRUE_VOID(success); michael@0: michael@0: unused << mRunnable.forget(); // picked up by callback handler michael@0: michael@0: /** michael@0: * FIXME: Bug 820274 michael@0: * michael@0: * If the user turns off Bluetooth in the middle of pairing process, michael@0: * the callback function GetObjectPathCallback may still be called michael@0: * while enabling next time by dbus daemon. To prevent this from michael@0: * happening, added a flag to distinguish if Bluetooth has been michael@0: * turned off. Nevertheless, we need a check if there is a better michael@0: * solution. michael@0: * michael@0: * Please see Bug 818696 for more information. michael@0: */ michael@0: sIsPairing++; michael@0: } michael@0: michael@0: private: michael@0: const nsCString mDeviceAddress; michael@0: int mTimeout; michael@0: nsRefPtr mRunnable; michael@0: }; michael@0: michael@0: nsresult michael@0: BluetoothDBusService::CreatePairedDeviceInternal( michael@0: const nsAString& aDeviceAddress, michael@0: int aTimeout, michael@0: BluetoothReplyRunnable* aRunnable) michael@0: { michael@0: Task* task = new CreatePairedDeviceInternalTask( michael@0: NS_ConvertUTF16toUTF8(aDeviceAddress), michael@0: aTimeout, aRunnable); michael@0: DispatchToDBusThread(task); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: class RemoveDeviceTask : public Task michael@0: { michael@0: public: michael@0: RemoveDeviceTask(const nsAString& aDeviceAddress, michael@0: BluetoothReplyRunnable* aRunnable) michael@0: : mDeviceAddress(aDeviceAddress) michael@0: , mRunnable(aRunnable) michael@0: { michael@0: MOZ_ASSERT(!mDeviceAddress.IsEmpty()); michael@0: MOZ_ASSERT(mRunnable); michael@0: } michael@0: michael@0: void Run() MOZ_OVERRIDE michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); // I/O thread michael@0: MOZ_ASSERT(sDBusConnection); michael@0: MOZ_ASSERT(!sAdapterPath.IsEmpty()); michael@0: michael@0: nsCString deviceObjectPath = michael@0: NS_ConvertUTF16toUTF8(GetObjectPathFromAddress(sAdapterPath, michael@0: mDeviceAddress)); michael@0: const char* cstrDeviceObjectPath = deviceObjectPath.get(); michael@0: michael@0: bool success = sDBusConnection->SendWithReply( michael@0: OnRemoveDeviceReply, static_cast(mRunnable.get()), -1, michael@0: BLUEZ_DBUS_BASE_IFC, michael@0: NS_ConvertUTF16toUTF8(sAdapterPath).get(), michael@0: DBUS_ADAPTER_IFACE, "RemoveDevice", michael@0: DBUS_TYPE_OBJECT_PATH, &cstrDeviceObjectPath, michael@0: DBUS_TYPE_INVALID); michael@0: NS_ENSURE_TRUE_VOID(success); michael@0: michael@0: unused << mRunnable.forget(); // picked up by callback handler michael@0: } michael@0: michael@0: protected: michael@0: static void OnRemoveDeviceReply(DBusMessage* aReply, void* aData) michael@0: { michael@0: nsAutoString errorStr; michael@0: michael@0: if (!aReply) { michael@0: errorStr.AssignLiteral("RemoveDevice failed"); michael@0: } michael@0: michael@0: nsRefPtr runnable = michael@0: dont_AddRef( michael@0: static_cast(aData)); michael@0: michael@0: DispatchBluetoothReply(runnable.get(), BluetoothValue(true), errorStr); michael@0: } michael@0: michael@0: private: michael@0: const nsString mDeviceAddress; michael@0: nsRefPtr mRunnable; michael@0: }; michael@0: michael@0: nsresult michael@0: BluetoothDBusService::RemoveDeviceInternal(const nsAString& aDeviceAddress, michael@0: BluetoothReplyRunnable* aRunnable) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: if (!IsReady()) { michael@0: NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!"); michael@0: DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr); michael@0: return NS_OK; michael@0: } michael@0: michael@0: Task* task = new RemoveDeviceTask(aDeviceAddress, aRunnable); michael@0: DispatchToDBusThread(task); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: class SetPinCodeTask : public Task michael@0: { michael@0: public: michael@0: SetPinCodeTask(const nsAString& aDeviceAddress, michael@0: const nsACString& aPinCode, michael@0: BluetoothReplyRunnable* aRunnable) michael@0: : mDeviceAddress(aDeviceAddress) michael@0: , mPinCode(aPinCode) michael@0: , mRunnable(aRunnable) michael@0: { michael@0: MOZ_ASSERT(!mDeviceAddress.IsEmpty()); michael@0: MOZ_ASSERT(mRunnable); michael@0: } michael@0: michael@0: void Run() MOZ_OVERRIDE michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); // I/O thread michael@0: michael@0: nsAutoString errorStr; michael@0: BluetoothValue v = true; michael@0: DBusMessage *msg; michael@0: if (!sPairingReqTable->Get(mDeviceAddress, &msg)) { michael@0: BT_WARNING("%s: Couldn't get original request message.", __FUNCTION__); michael@0: errorStr.AssignLiteral("Couldn't get original request message."); michael@0: DispatchBluetoothReply(mRunnable, v, errorStr); michael@0: return; michael@0: } michael@0: michael@0: DBusMessage *reply = dbus_message_new_method_return(msg); michael@0: michael@0: if (!reply) { michael@0: BT_WARNING("%s: Memory can't be allocated for the message.", __FUNCTION__); michael@0: dbus_message_unref(msg); michael@0: errorStr.AssignLiteral("Memory can't be allocated for the message."); michael@0: DispatchBluetoothReply(mRunnable, v, errorStr); michael@0: return; michael@0: } michael@0: michael@0: const char* pinCode = mPinCode.get(); michael@0: michael@0: if (!dbus_message_append_args(reply, michael@0: DBUS_TYPE_STRING, &pinCode, michael@0: DBUS_TYPE_INVALID)) { michael@0: BT_WARNING("%s: Couldn't append arguments to dbus message.", __FUNCTION__); michael@0: errorStr.AssignLiteral("Couldn't append arguments to dbus message."); michael@0: } else { michael@0: MOZ_ASSERT(sDBusConnection); michael@0: sDBusConnection->Send(reply); michael@0: } michael@0: michael@0: dbus_message_unref(msg); michael@0: dbus_message_unref(reply); michael@0: michael@0: sPairingReqTable->Remove(mDeviceAddress); michael@0: DispatchBluetoothReply(mRunnable, v, errorStr); michael@0: } michael@0: michael@0: private: michael@0: const nsString mDeviceAddress; michael@0: const nsCString mPinCode; michael@0: nsRefPtr mRunnable; michael@0: }; michael@0: michael@0: bool michael@0: BluetoothDBusService::SetPinCodeInternal(const nsAString& aDeviceAddress, michael@0: const nsAString& aPinCode, michael@0: BluetoothReplyRunnable* aRunnable) michael@0: { michael@0: Task* task = new SetPinCodeTask(aDeviceAddress, michael@0: NS_ConvertUTF16toUTF8(aPinCode), michael@0: aRunnable); michael@0: DispatchToDBusThread(task); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: class SetPasskeyTask : public Task michael@0: { michael@0: public: michael@0: SetPasskeyTask(const nsAString& aDeviceAddress, michael@0: uint32_t aPasskey, michael@0: BluetoothReplyRunnable* aRunnable) michael@0: : mDeviceAddress(aDeviceAddress) michael@0: , mPasskey(aPasskey) michael@0: , mRunnable(aRunnable) michael@0: { michael@0: MOZ_ASSERT(!mDeviceAddress.IsEmpty()); michael@0: MOZ_ASSERT(mRunnable); michael@0: } michael@0: michael@0: void Run() MOZ_OVERRIDE michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); // I/O thread michael@0: michael@0: nsAutoString errorStr; michael@0: BluetoothValue v = true; michael@0: DBusMessage *msg; michael@0: if (!sPairingReqTable->Get(mDeviceAddress, &msg)) { michael@0: BT_WARNING("%s: Couldn't get original request message.", __FUNCTION__); michael@0: errorStr.AssignLiteral("Couldn't get original request message."); michael@0: DispatchBluetoothReply(mRunnable, v, errorStr); michael@0: return; michael@0: } michael@0: michael@0: DBusMessage *reply = dbus_message_new_method_return(msg); michael@0: michael@0: if (!reply) { michael@0: BT_WARNING("%s: Memory can't be allocated for the message.", __FUNCTION__); michael@0: dbus_message_unref(msg); michael@0: errorStr.AssignLiteral("Memory can't be allocated for the message."); michael@0: DispatchBluetoothReply(mRunnable, v, errorStr); michael@0: return; michael@0: } michael@0: michael@0: uint32_t passkey = mPasskey; michael@0: michael@0: if (!dbus_message_append_args(reply, michael@0: DBUS_TYPE_UINT32, &passkey, michael@0: DBUS_TYPE_INVALID)) { michael@0: BT_WARNING("%s: Couldn't append arguments to dbus message.", __FUNCTION__); michael@0: errorStr.AssignLiteral("Couldn't append arguments to dbus message."); michael@0: } else { michael@0: MOZ_ASSERT(sDBusConnection); michael@0: sDBusConnection->Send(reply); michael@0: } michael@0: michael@0: dbus_message_unref(msg); michael@0: dbus_message_unref(reply); michael@0: michael@0: sPairingReqTable->Remove(mDeviceAddress); michael@0: DispatchBluetoothReply(mRunnable, v, errorStr); michael@0: } michael@0: michael@0: private: michael@0: nsString mDeviceAddress; michael@0: uint32_t mPasskey; michael@0: nsRefPtr mRunnable; michael@0: }; michael@0: michael@0: bool michael@0: BluetoothDBusService::SetPasskeyInternal(const nsAString& aDeviceAddress, michael@0: uint32_t aPasskey, michael@0: BluetoothReplyRunnable* aRunnable) michael@0: { michael@0: Task* task = new SetPasskeyTask(aDeviceAddress, michael@0: aPasskey, michael@0: aRunnable); michael@0: DispatchToDBusThread(task); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: michael@0: bool michael@0: BluetoothDBusService::SetPairingConfirmationInternal( michael@0: const nsAString& aDeviceAddress, michael@0: bool aConfirm, michael@0: BluetoothReplyRunnable* aRunnable) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: Task* task = new SetPairingConfirmationTask(aDeviceAddress, michael@0: aConfirm, michael@0: aRunnable); michael@0: DispatchToDBusThread(task); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: static void michael@0: NextBluetoothProfileController() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: // First, remove the task at the front which has been already done. michael@0: NS_ENSURE_FALSE_VOID(sControllerArray.IsEmpty()); michael@0: sControllerArray.RemoveElementAt(0); michael@0: michael@0: // Re-check if the task array is empty, if it's not, the next task will begin. michael@0: NS_ENSURE_FALSE_VOID(sControllerArray.IsEmpty()); michael@0: sControllerArray[0]->StartSession(); michael@0: } michael@0: michael@0: static void michael@0: ConnectDisconnect(bool aConnect, const nsAString& aDeviceAddress, michael@0: BluetoothReplyRunnable* aRunnable, michael@0: uint16_t aServiceUuid, uint32_t aCod = 0) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_ASSERT(aRunnable); michael@0: michael@0: BluetoothProfileController* controller = michael@0: new BluetoothProfileController(aConnect, aDeviceAddress, aRunnable, michael@0: NextBluetoothProfileController, michael@0: aServiceUuid, aCod); michael@0: sControllerArray.AppendElement(controller); michael@0: michael@0: /** michael@0: * If the request is the first element of the quene, start from here. Note michael@0: * that other request is pushed into the quene and is popped out after the michael@0: * first one is completed. See NextBluetoothProfileController() for details. michael@0: */ michael@0: if (sControllerArray.Length() == 1) { michael@0: sControllerArray[0]->StartSession(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: BluetoothDBusService::Connect(const nsAString& aDeviceAddress, michael@0: uint32_t aCod, michael@0: uint16_t aServiceUuid, michael@0: BluetoothReplyRunnable* aRunnable) michael@0: { michael@0: ConnectDisconnect(true, aDeviceAddress, aRunnable, aServiceUuid, aCod); michael@0: } michael@0: michael@0: void michael@0: BluetoothDBusService::Disconnect(const nsAString& aDeviceAddress, michael@0: uint16_t aServiceUuid, michael@0: BluetoothReplyRunnable* aRunnable) michael@0: { michael@0: ConnectDisconnect(false, aDeviceAddress, aRunnable, aServiceUuid); michael@0: } michael@0: michael@0: bool michael@0: BluetoothDBusService::IsConnected(const uint16_t aServiceUuid) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: BluetoothProfileManagerBase* profile = michael@0: BluetoothUuidHelper::GetBluetoothProfileManager(aServiceUuid); michael@0: if (!profile) { michael@0: BT_WARNING(ERR_UNKNOWN_PROFILE); michael@0: return false; michael@0: } michael@0: michael@0: NS_ENSURE_TRUE(profile, false); michael@0: return profile->IsConnected(); michael@0: } michael@0: michael@0: #ifdef MOZ_B2G_RIL michael@0: void michael@0: BluetoothDBusService::AnswerWaitingCall(BluetoothReplyRunnable* aRunnable) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: BluetoothHfpManager* hfp = BluetoothHfpManager::Get(); michael@0: hfp->AnswerWaitingCall(); michael@0: michael@0: DispatchBluetoothReply(aRunnable, BluetoothValue(true), EmptyString()); michael@0: } michael@0: michael@0: void michael@0: BluetoothDBusService::IgnoreWaitingCall(BluetoothReplyRunnable* aRunnable) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: BluetoothHfpManager* hfp = BluetoothHfpManager::Get(); michael@0: hfp->IgnoreWaitingCall(); michael@0: michael@0: DispatchBluetoothReply(aRunnable, BluetoothValue(true), EmptyString()); michael@0: } michael@0: michael@0: void michael@0: BluetoothDBusService::ToggleCalls(BluetoothReplyRunnable* aRunnable) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: BluetoothHfpManager* hfp = BluetoothHfpManager::Get(); michael@0: hfp->ToggleCalls(); michael@0: michael@0: DispatchBluetoothReply(aRunnable, BluetoothValue(true), EmptyString()); michael@0: } michael@0: #endif // MOZ_B2G_RIL michael@0: michael@0: class OnUpdateSdpRecordsRunnable : public nsRunnable michael@0: { michael@0: public: michael@0: OnUpdateSdpRecordsRunnable(const nsAString& aObjectPath, michael@0: BluetoothProfileManagerBase* aManager) michael@0: : mManager(aManager) michael@0: { michael@0: MOZ_ASSERT(!aObjectPath.IsEmpty()); michael@0: MOZ_ASSERT(aManager); michael@0: michael@0: mDeviceAddress = GetAddressFromObjectPath(aObjectPath); michael@0: } michael@0: michael@0: nsresult michael@0: Run() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: mManager->OnUpdateSdpRecords(mDeviceAddress); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: nsString mDeviceAddress; michael@0: BluetoothProfileManagerBase* mManager; michael@0: }; michael@0: michael@0: class OnGetServiceChannelRunnable : public nsRunnable michael@0: { michael@0: public: michael@0: OnGetServiceChannelRunnable(const nsAString& aDeviceAddress, michael@0: const nsAString& aServiceUuid, michael@0: int aChannel, michael@0: BluetoothProfileManagerBase* aManager) michael@0: : mDeviceAddress(aDeviceAddress) michael@0: , mServiceUuid(aServiceUuid) michael@0: , mChannel(aChannel) michael@0: , mManager(aManager) michael@0: { michael@0: MOZ_ASSERT(!aDeviceAddress.IsEmpty()); michael@0: MOZ_ASSERT(!aServiceUuid.IsEmpty()); michael@0: MOZ_ASSERT(aManager); michael@0: } michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: mManager->OnGetServiceChannel(mDeviceAddress, mServiceUuid, mChannel); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: nsString mDeviceAddress; michael@0: nsString mServiceUuid; michael@0: int mChannel; michael@0: BluetoothProfileManagerBase* mManager; michael@0: }; michael@0: michael@0: class OnGetServiceChannelReplyHandler : public DBusReplyHandler michael@0: { michael@0: public: michael@0: OnGetServiceChannelReplyHandler(const nsAString& aDeviceAddress, michael@0: const nsAString& aServiceUUID, michael@0: BluetoothProfileManagerBase* aBluetoothProfileManager) michael@0: : mDeviceAddress(aDeviceAddress), michael@0: mServiceUUID(aServiceUUID), michael@0: mBluetoothProfileManager(aBluetoothProfileManager) michael@0: { michael@0: MOZ_ASSERT(mBluetoothProfileManager); michael@0: } michael@0: michael@0: void Handle(DBusMessage* aReply) michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); // I/O thread michael@0: michael@0: // The default channel is an invalid value of -1. We michael@0: // update it if we have received a correct reply. Both michael@0: // cases, valid and invalid channel numbers, are handled michael@0: // in BluetoothProfileManagerBase::OnGetServiceChannel. michael@0: michael@0: int channel = -1; michael@0: michael@0: if (aReply && (dbus_message_get_type(aReply) != DBUS_MESSAGE_TYPE_ERROR)) { michael@0: channel = dbus_returns_int32(aReply); michael@0: } michael@0: michael@0: nsRefPtr r = new OnGetServiceChannelRunnable(mDeviceAddress, michael@0: mServiceUUID, michael@0: channel, michael@0: mBluetoothProfileManager); michael@0: nsresult rv = NS_DispatchToMainThread(r); michael@0: NS_ENSURE_SUCCESS_VOID(rv); michael@0: } michael@0: michael@0: private: michael@0: nsString mDeviceAddress; michael@0: nsString mServiceUUID; michael@0: BluetoothProfileManagerBase* mBluetoothProfileManager; michael@0: }; michael@0: michael@0: class GetServiceChannelTask : public Task michael@0: { michael@0: public: michael@0: GetServiceChannelTask(const nsAString& aDeviceAddress, michael@0: const nsAString& aServiceUUID, michael@0: BluetoothProfileManagerBase* aBluetoothProfileManager) michael@0: : mDeviceAddress(aDeviceAddress) michael@0: , mServiceUUID(aServiceUUID) michael@0: , mBluetoothProfileManager(aBluetoothProfileManager) michael@0: { michael@0: MOZ_ASSERT(!mDeviceAddress.IsEmpty()); michael@0: MOZ_ASSERT(mBluetoothProfileManager); michael@0: } michael@0: michael@0: void Run() MOZ_OVERRIDE michael@0: { michael@0: static const int sProtocolDescriptorList = 0x0004; michael@0: michael@0: MOZ_ASSERT(!NS_IsMainThread()); // I/O thread michael@0: MOZ_ASSERT(sDBusConnection); michael@0: MOZ_ASSERT(!sAdapterPath.IsEmpty()); michael@0: michael@0: nsString objectPath = michael@0: GetObjectPathFromAddress(sAdapterPath, mDeviceAddress); michael@0: michael@0: nsRefPtr handler = michael@0: new OnGetServiceChannelReplyHandler(mDeviceAddress, mServiceUUID, michael@0: mBluetoothProfileManager); michael@0: michael@0: nsCString serviceUUID = NS_ConvertUTF16toUTF8(mServiceUUID); michael@0: const char* cstrServiceUUID = serviceUUID.get(); michael@0: michael@0: bool success = sDBusConnection->SendWithReply( michael@0: OnGetServiceChannelReplyHandler::Callback, handler, -1, michael@0: BLUEZ_DBUS_BASE_IFC, michael@0: NS_ConvertUTF16toUTF8(objectPath).get(), michael@0: DBUS_DEVICE_IFACE, "GetServiceAttributeValue", michael@0: DBUS_TYPE_STRING, &cstrServiceUUID, michael@0: DBUS_TYPE_UINT16, &sProtocolDescriptorList, michael@0: DBUS_TYPE_INVALID); michael@0: NS_ENSURE_TRUE_VOID(success); michael@0: michael@0: unused << handler.forget(); // picked up by callback handler michael@0: } michael@0: michael@0: private: michael@0: nsString mDeviceAddress; michael@0: nsString mServiceUUID; michael@0: BluetoothProfileManagerBase* mBluetoothProfileManager; michael@0: }; michael@0: michael@0: nsresult michael@0: BluetoothDBusService::GetServiceChannel(const nsAString& aDeviceAddress, michael@0: const nsAString& aServiceUUID, michael@0: BluetoothProfileManagerBase* aManager) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: if (!IsReady()) { michael@0: NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!"); michael@0: return NS_OK; michael@0: } michael@0: michael@0: #ifdef MOZ_WIDGET_GONK michael@0: // GetServiceAttributeValue only exists in android's bluez dbus binding michael@0: // implementation michael@0: Task* task = new GetServiceChannelTask(aDeviceAddress, michael@0: aServiceUUID, michael@0: aManager); michael@0: DispatchToDBusThread(task); michael@0: #else michael@0: // FIXME/Bug 793977 qdot: Just set something for desktop, until we have a michael@0: // parser for the GetServiceAttributes xml block michael@0: // michael@0: // Even though we are on the main thread already, we need to dispatch a michael@0: // runnable here. OnGetServiceChannel needs mRunnable to be set, which michael@0: // happens after GetServiceChannel returns. michael@0: nsRefPtr r = new OnGetServiceChannelRunnable(aDeviceAddress, michael@0: aServiceUUID, michael@0: 1, michael@0: aManager); michael@0: NS_DispatchToMainThread(r); michael@0: #endif michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: class UpdateSdpRecordsTask : public Task michael@0: { michael@0: public: michael@0: UpdateSdpRecordsTask(const nsAString& aDeviceAddress, michael@0: BluetoothProfileManagerBase* aBluetoothProfileManager) michael@0: : mDeviceAddress(aDeviceAddress) michael@0: , mBluetoothProfileManager(aBluetoothProfileManager) michael@0: { michael@0: MOZ_ASSERT(!mDeviceAddress.IsEmpty()); michael@0: MOZ_ASSERT(mBluetoothProfileManager); michael@0: } michael@0: michael@0: void Run() MOZ_OVERRIDE michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); // I/O thread michael@0: MOZ_ASSERT(sDBusConnection); michael@0: MOZ_ASSERT(!sAdapterPath.IsEmpty()); michael@0: michael@0: const nsString objectPath = michael@0: GetObjectPathFromAddress(sAdapterPath, mDeviceAddress); michael@0: michael@0: // I choose to use raw pointer here because this is going to be passed as an michael@0: // argument into SendWithReply() at once. michael@0: OnUpdateSdpRecordsRunnable* callbackRunnable = michael@0: new OnUpdateSdpRecordsRunnable(objectPath, mBluetoothProfileManager); michael@0: michael@0: sDBusConnection->SendWithReply(DiscoverServicesCallback, michael@0: (void*)callbackRunnable, -1, michael@0: BLUEZ_DBUS_BASE_IFC, michael@0: NS_ConvertUTF16toUTF8(objectPath).get(), michael@0: DBUS_DEVICE_IFACE, michael@0: "DiscoverServices", michael@0: DBUS_TYPE_STRING, &EmptyCString(), michael@0: DBUS_TYPE_INVALID); michael@0: } michael@0: michael@0: protected: michael@0: static void DiscoverServicesCallback(DBusMessage* aMsg, void* aData) michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); // I/O thread michael@0: michael@0: nsRefPtr r( michael@0: static_cast(aData)); michael@0: NS_DispatchToMainThread(r); michael@0: } michael@0: michael@0: private: michael@0: const nsString mDeviceAddress; michael@0: BluetoothProfileManagerBase* mBluetoothProfileManager; michael@0: }; michael@0: michael@0: bool michael@0: BluetoothDBusService::UpdateSdpRecords(const nsAString& aDeviceAddress, michael@0: BluetoothProfileManagerBase* aManager) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: Task* task = new UpdateSdpRecordsTask(aDeviceAddress, aManager); michael@0: DispatchToDBusThread(task); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: BluetoothDBusService::SendFile(const nsAString& aDeviceAddress, michael@0: BlobParent* aBlobParent, michael@0: BlobChild* aBlobChild, michael@0: BluetoothReplyRunnable* aRunnable) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: // Currently we only support one device sending one file at a time, michael@0: // so we don't need aDeviceAddress here because the target device michael@0: // has been determined when calling 'Connect()'. Nevertheless, keep michael@0: // it for future use. michael@0: BluetoothOppManager* opp = BluetoothOppManager::Get(); michael@0: nsAutoString errorStr; michael@0: if (!opp || !opp->SendFile(aDeviceAddress, aBlobParent)) { michael@0: errorStr.AssignLiteral("Calling SendFile() failed"); michael@0: } michael@0: michael@0: DispatchBluetoothReply(aRunnable, BluetoothValue(true), errorStr); michael@0: } michael@0: michael@0: void michael@0: BluetoothDBusService::SendFile(const nsAString& aDeviceAddress, michael@0: nsIDOMBlob* aBlob, michael@0: BluetoothReplyRunnable* aRunnable) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: // Currently we only support one device sending one file at a time, michael@0: // so we don't need aDeviceAddress here because the target device michael@0: // has been determined when calling 'Connect()'. Nevertheless, keep michael@0: // it for future use. michael@0: BluetoothOppManager* opp = BluetoothOppManager::Get(); michael@0: nsAutoString errorStr; michael@0: if (!opp || !opp->SendFile(aDeviceAddress, aBlob)) { michael@0: errorStr.AssignLiteral("Calling SendFile() failed"); michael@0: } michael@0: michael@0: DispatchBluetoothReply(aRunnable, BluetoothValue(true), errorStr); michael@0: } michael@0: michael@0: void michael@0: BluetoothDBusService::StopSendingFile(const nsAString& aDeviceAddress, michael@0: BluetoothReplyRunnable* aRunnable) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: // Currently we only support one device sending one file at a time, michael@0: // so we don't need aDeviceAddress here because the target device michael@0: // has been determined when calling 'Connect()'. Nevertheless, keep michael@0: // it for future use. michael@0: BluetoothOppManager* opp = BluetoothOppManager::Get(); michael@0: nsAutoString errorStr; michael@0: if (!opp || !opp->StopSendingFile()) { michael@0: errorStr.AssignLiteral("Calling StopSendingFile() failed"); michael@0: } michael@0: michael@0: DispatchBluetoothReply(aRunnable, BluetoothValue(true), errorStr); michael@0: } michael@0: michael@0: void michael@0: BluetoothDBusService::ConfirmReceivingFile(const nsAString& aDeviceAddress, michael@0: bool aConfirm, michael@0: BluetoothReplyRunnable* aRunnable) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread(), "Must be called from main thread!"); michael@0: michael@0: // Currently we only support one device sending one file at a time, michael@0: // so we don't need aDeviceAddress here because the target device michael@0: // has been determined when calling 'Connect()'. Nevertheless, keep michael@0: // it for future use. michael@0: BluetoothOppManager* opp = BluetoothOppManager::Get(); michael@0: nsAutoString errorStr; michael@0: if (!opp || !opp->ConfirmReceivingFile(aConfirm)) { michael@0: errorStr.AssignLiteral("Calling ConfirmReceivingFile() failed"); michael@0: } michael@0: michael@0: DispatchBluetoothReply(aRunnable, BluetoothValue(true), errorStr); michael@0: } michael@0: michael@0: void michael@0: BluetoothDBusService::ConnectSco(BluetoothReplyRunnable* aRunnable) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: BluetoothHfpManager* hfp = BluetoothHfpManager::Get(); michael@0: if (!hfp || !hfp->ConnectSco(aRunnable)) { michael@0: NS_NAMED_LITERAL_STRING(replyError, "Calling ConnectSco() failed"); michael@0: DispatchBluetoothReply(aRunnable, BluetoothValue(), replyError); michael@0: } michael@0: } michael@0: michael@0: void michael@0: BluetoothDBusService::DisconnectSco(BluetoothReplyRunnable* aRunnable) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: BluetoothHfpManager* hfp = BluetoothHfpManager::Get(); michael@0: if (!hfp || !hfp->DisconnectSco()) { michael@0: NS_NAMED_LITERAL_STRING(replyError, "Calling DisconnectSco() failed"); michael@0: DispatchBluetoothReply(aRunnable, BluetoothValue(), replyError); michael@0: return; michael@0: } michael@0: michael@0: DispatchBluetoothReply(aRunnable, BluetoothValue(true), EmptyString()); michael@0: } michael@0: michael@0: void michael@0: BluetoothDBusService::IsScoConnected(BluetoothReplyRunnable* aRunnable) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: BluetoothHfpManager* hfp = BluetoothHfpManager::Get(); michael@0: if (!hfp) { michael@0: NS_NAMED_LITERAL_STRING(replyError, "Fail to get BluetoothHfpManager"); michael@0: DispatchBluetoothReply(aRunnable, BluetoothValue(), replyError); michael@0: return; michael@0: } michael@0: michael@0: DispatchBluetoothReply(aRunnable, hfp->IsScoConnected(), EmptyString()); michael@0: } michael@0: michael@0: class SendMetadataTask : public Task michael@0: { michael@0: public: michael@0: SendMetadataTask(const nsAString& aDeviceAddress, michael@0: const nsACString& aTitle, michael@0: const nsACString& aArtist, michael@0: const nsACString& aAlbum, michael@0: int64_t aMediaNumber, michael@0: int64_t aTotalMediaCount, michael@0: int64_t aDuration, michael@0: BluetoothReplyRunnable* aRunnable) michael@0: : mDeviceAddress(aDeviceAddress) michael@0: , mTitle(aTitle) michael@0: , mArtist(aArtist) michael@0: , mAlbum(aAlbum) michael@0: , mMediaNumber(aMediaNumber) michael@0: , mTotalMediaCount(aTotalMediaCount) michael@0: , mDuration(aDuration) michael@0: , mRunnable(aRunnable) michael@0: { michael@0: MOZ_ASSERT(!mDeviceAddress.IsEmpty()); michael@0: MOZ_ASSERT(mRunnable); michael@0: } michael@0: michael@0: void Run() MOZ_OVERRIDE michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); // I/O thread michael@0: MOZ_ASSERT(sDBusConnection); michael@0: MOZ_ASSERT(!sAdapterPath.IsEmpty()); michael@0: michael@0: // We currently don't support genre field in music player. michael@0: // In order to send media metadata through AVRCP, we set genre to an empty michael@0: // string to match the BlueZ method "UpdateMetaData" with signature "sssssss", michael@0: // which takes genre field as the last parameter. michael@0: nsCString tempGenre = EmptyCString(); michael@0: nsCString tempMediaNumber = EmptyCString(); michael@0: nsCString tempTotalMediaCount = EmptyCString(); michael@0: nsCString tempDuration = EmptyCString(); michael@0: michael@0: if (mMediaNumber >= 0) { michael@0: tempMediaNumber.AppendInt(mMediaNumber); michael@0: } michael@0: if (mTotalMediaCount >= 0) { michael@0: tempTotalMediaCount.AppendInt(mTotalMediaCount); michael@0: } michael@0: if (mDuration >= 0) { michael@0: tempDuration.AppendInt(mDuration); michael@0: } michael@0: michael@0: const nsCString objectPath = NS_ConvertUTF16toUTF8( michael@0: GetObjectPathFromAddress(sAdapterPath, mDeviceAddress)); michael@0: michael@0: const char* title = mTitle.get(); michael@0: const char* album = mAlbum.get(); michael@0: const char* artist = mArtist.get(); michael@0: const char* mediaNumber = tempMediaNumber.get(); michael@0: const char* totalMediaCount = tempTotalMediaCount.get(); michael@0: const char* duration = tempDuration.get(); michael@0: const char* genre = tempGenre.get(); michael@0: michael@0: bool success = sDBusConnection->SendWithReply( michael@0: GetVoidCallback, static_cast(mRunnable.get()), -1, michael@0: BLUEZ_DBUS_BASE_IFC, michael@0: objectPath.get(), michael@0: DBUS_CTL_IFACE, "UpdateMetaData", michael@0: DBUS_TYPE_STRING, &title, michael@0: DBUS_TYPE_STRING, &artist, michael@0: DBUS_TYPE_STRING, &album, michael@0: DBUS_TYPE_STRING, &mediaNumber, michael@0: DBUS_TYPE_STRING, &totalMediaCount, michael@0: DBUS_TYPE_STRING, &duration, michael@0: DBUS_TYPE_STRING, &genre, michael@0: DBUS_TYPE_INVALID); michael@0: NS_ENSURE_TRUE_VOID(success); michael@0: michael@0: unused << mRunnable.forget(); // picked up by callback handler michael@0: } michael@0: michael@0: private: michael@0: const nsString mDeviceAddress; michael@0: const nsCString mTitle; michael@0: const nsCString mArtist; michael@0: const nsCString mAlbum; michael@0: int64_t mMediaNumber; michael@0: int64_t mTotalMediaCount; michael@0: int64_t mDuration; michael@0: nsRefPtr mRunnable; michael@0: }; michael@0: michael@0: void michael@0: BluetoothDBusService::SendMetaData(const nsAString& aTitle, michael@0: const nsAString& aArtist, michael@0: const nsAString& aAlbum, michael@0: int64_t aMediaNumber, michael@0: int64_t aTotalMediaCount, michael@0: int64_t aDuration, michael@0: BluetoothReplyRunnable* aRunnable) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: if (!IsReady()) { michael@0: NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!"); michael@0: DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr); michael@0: return; michael@0: } michael@0: michael@0: BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get(); michael@0: NS_ENSURE_TRUE_VOID(a2dp); michael@0: michael@0: if (!a2dp->IsConnected()) { michael@0: DispatchBluetoothReply(aRunnable, BluetoothValue(), michael@0: NS_LITERAL_STRING(ERR_A2DP_IS_DISCONNECTED)); michael@0: return; michael@0: } else if (!a2dp->IsAvrcpConnected()) { michael@0: DispatchBluetoothReply(aRunnable, BluetoothValue(), michael@0: NS_LITERAL_STRING(ERR_AVRCP_IS_DISCONNECTED)); michael@0: return; michael@0: } michael@0: michael@0: nsAutoString prevTitle, prevAlbum; michael@0: a2dp->GetTitle(prevTitle); michael@0: a2dp->GetAlbum(prevAlbum); michael@0: michael@0: if (aMediaNumber != a2dp->GetMediaNumber() || michael@0: !aTitle.Equals(prevTitle) || michael@0: !aAlbum.Equals(prevAlbum)) { michael@0: UpdateNotification(ControlEventId::EVENT_TRACK_CHANGED, aMediaNumber); michael@0: } michael@0: michael@0: nsAutoString deviceAddress; michael@0: a2dp->GetAddress(deviceAddress); michael@0: michael@0: Task* task = new SendMetadataTask( michael@0: deviceAddress, michael@0: NS_ConvertUTF16toUTF8(aTitle), michael@0: NS_ConvertUTF16toUTF8(aArtist), michael@0: NS_ConvertUTF16toUTF8(aAlbum), michael@0: aMediaNumber, michael@0: aTotalMediaCount, michael@0: aDuration, michael@0: aRunnable); michael@0: DispatchToDBusThread(task); michael@0: michael@0: a2dp->UpdateMetaData(aTitle, aArtist, aAlbum, michael@0: aMediaNumber, aTotalMediaCount, aDuration); michael@0: } michael@0: michael@0: static ControlPlayStatus michael@0: PlayStatusStringToControlPlayStatus(const nsAString& aPlayStatus) michael@0: { michael@0: ControlPlayStatus playStatus = ControlPlayStatus::PLAYSTATUS_UNKNOWN; michael@0: if (aPlayStatus.EqualsLiteral("STOPPED")) { michael@0: playStatus = ControlPlayStatus::PLAYSTATUS_STOPPED; michael@0: } else if (aPlayStatus.EqualsLiteral("PLAYING")) { michael@0: playStatus = ControlPlayStatus::PLAYSTATUS_PLAYING; michael@0: } else if (aPlayStatus.EqualsLiteral("PAUSED")) { michael@0: playStatus = ControlPlayStatus::PLAYSTATUS_PAUSED; michael@0: } else if (aPlayStatus.EqualsLiteral("FWD_SEEK")) { michael@0: playStatus = ControlPlayStatus::PLAYSTATUS_FWD_SEEK; michael@0: } else if (aPlayStatus.EqualsLiteral("REV_SEEK")) { michael@0: playStatus = ControlPlayStatus::PLAYSTATUS_REV_SEEK; michael@0: } else if (aPlayStatus.EqualsLiteral("ERROR")) { michael@0: playStatus = ControlPlayStatus::PLAYSTATUS_ERROR; michael@0: } michael@0: michael@0: return playStatus; michael@0: } michael@0: michael@0: class SendPlayStatusTask : public Task michael@0: { michael@0: public: michael@0: SendPlayStatusTask(const nsAString& aDeviceAddress, michael@0: int64_t aDuration, michael@0: int64_t aPosition, michael@0: ControlPlayStatus aPlayStatus, michael@0: BluetoothReplyRunnable* aRunnable) michael@0: : mDeviceAddress(aDeviceAddress) michael@0: , mDuration(aDuration) michael@0: , mPosition(aPosition) michael@0: , mPlayStatus(aPlayStatus) michael@0: , mRunnable(aRunnable) michael@0: { michael@0: MOZ_ASSERT(!mDeviceAddress.IsEmpty()); michael@0: MOZ_ASSERT(mRunnable); michael@0: } michael@0: michael@0: void Run() MOZ_OVERRIDE michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); // I/O thread michael@0: MOZ_ASSERT(sDBusConnection); michael@0: MOZ_ASSERT(!sAdapterPath.IsEmpty()); michael@0: michael@0: const nsCString objectPath = NS_ConvertUTF16toUTF8( michael@0: GetObjectPathFromAddress(sAdapterPath, mDeviceAddress)); michael@0: michael@0: uint32_t tempPlayStatus = mPlayStatus; michael@0: michael@0: bool success = sDBusConnection->SendWithReply( michael@0: GetVoidCallback, static_cast(mRunnable.get()), -1, michael@0: BLUEZ_DBUS_BASE_IFC, michael@0: objectPath.get(), michael@0: DBUS_CTL_IFACE, "UpdatePlayStatus", michael@0: DBUS_TYPE_UINT32, &mDuration, michael@0: DBUS_TYPE_UINT32, &mPosition, michael@0: DBUS_TYPE_UINT32, &tempPlayStatus, michael@0: DBUS_TYPE_INVALID); michael@0: NS_ENSURE_TRUE_VOID(success); michael@0: michael@0: unused << mRunnable.forget(); // picked up by callback handler michael@0: } michael@0: michael@0: private: michael@0: const nsString mDeviceAddress; michael@0: int64_t mDuration; michael@0: int64_t mPosition; michael@0: ControlPlayStatus mPlayStatus; michael@0: nsRefPtr mRunnable; michael@0: }; michael@0: michael@0: void michael@0: BluetoothDBusService::SendPlayStatus(int64_t aDuration, michael@0: int64_t aPosition, michael@0: const nsAString& aPlayStatus, michael@0: BluetoothReplyRunnable* aRunnable) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: if (!IsReady()) { michael@0: NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!"); michael@0: DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr); michael@0: return; michael@0: } michael@0: michael@0: ControlPlayStatus playStatus = michael@0: PlayStatusStringToControlPlayStatus(aPlayStatus); michael@0: if (playStatus == ControlPlayStatus::PLAYSTATUS_UNKNOWN) { michael@0: DispatchBluetoothReply(aRunnable, BluetoothValue(), michael@0: NS_LITERAL_STRING("Invalid play status")); michael@0: return; michael@0: } else if (aDuration < 0) { michael@0: DispatchBluetoothReply(aRunnable, BluetoothValue(), michael@0: NS_LITERAL_STRING("Invalid duration")); michael@0: return; michael@0: } else if (aPosition < 0) { michael@0: DispatchBluetoothReply(aRunnable, BluetoothValue(), michael@0: NS_LITERAL_STRING("Invalid position")); michael@0: return; michael@0: } michael@0: michael@0: BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get(); michael@0: NS_ENSURE_TRUE_VOID(a2dp); michael@0: michael@0: if (!a2dp->IsConnected()) { michael@0: DispatchBluetoothReply(aRunnable, BluetoothValue(), michael@0: NS_LITERAL_STRING(ERR_A2DP_IS_DISCONNECTED)); michael@0: return; michael@0: } else if (!a2dp->IsAvrcpConnected()) { michael@0: DispatchBluetoothReply(aRunnable, BluetoothValue(), michael@0: NS_LITERAL_STRING(ERR_AVRCP_IS_DISCONNECTED)); michael@0: return; michael@0: } michael@0: michael@0: if (playStatus != a2dp->GetPlayStatus()) { michael@0: UpdateNotification(ControlEventId::EVENT_PLAYBACK_STATUS_CHANGED, michael@0: playStatus); michael@0: } else if (aPosition != a2dp->GetPosition()) { michael@0: UpdateNotification(ControlEventId::EVENT_PLAYBACK_POS_CHANGED, aPosition); michael@0: } michael@0: michael@0: nsAutoString deviceAddress; michael@0: a2dp->GetAddress(deviceAddress); michael@0: michael@0: Task* task = new SendPlayStatusTask(deviceAddress, michael@0: aDuration, michael@0: aPosition, michael@0: playStatus, michael@0: aRunnable); michael@0: DispatchToDBusThread(task); michael@0: michael@0: a2dp->UpdatePlayStatus(aDuration, aPosition, playStatus); michael@0: } michael@0: michael@0: static void michael@0: ControlCallback(DBusMessage* aMsg, void* aParam) michael@0: { michael@0: NS_ENSURE_TRUE_VOID(aMsg); michael@0: michael@0: BluetoothValue v; michael@0: nsAutoString replyError; michael@0: UnpackVoidMessage(aMsg, nullptr, v, replyError); michael@0: if (!v.get_bool()) { michael@0: BT_WARNING(NS_ConvertUTF16toUTF8(replyError).get()); michael@0: } michael@0: } michael@0: michael@0: class UpdatePlayStatusTask : public Task michael@0: { michael@0: public: michael@0: UpdatePlayStatusTask(const nsAString& aDeviceAddress, michael@0: int32_t aDuration, michael@0: int32_t aPosition, michael@0: ControlPlayStatus aPlayStatus) michael@0: : mDeviceAddress(aDeviceAddress) michael@0: , mDuration(aDuration) michael@0: , mPosition(aPosition) michael@0: , mPlayStatus(aPlayStatus) michael@0: { michael@0: MOZ_ASSERT(!mDeviceAddress.IsEmpty()); michael@0: } michael@0: michael@0: void Run() MOZ_OVERRIDE michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); // I/O thread michael@0: MOZ_ASSERT(sDBusConnection); michael@0: MOZ_ASSERT(!sAdapterPath.IsEmpty()); michael@0: michael@0: const nsCString objectPath = NS_ConvertUTF16toUTF8( michael@0: GetObjectPathFromAddress(sAdapterPath, mDeviceAddress)); michael@0: michael@0: uint32_t tempPlayStatus = mPlayStatus; michael@0: michael@0: bool success = sDBusConnection->SendWithReply( michael@0: ControlCallback, nullptr, -1, michael@0: BLUEZ_DBUS_BASE_IFC, michael@0: objectPath.get(), michael@0: DBUS_CTL_IFACE, "UpdatePlayStatus", michael@0: DBUS_TYPE_UINT32, &mDuration, michael@0: DBUS_TYPE_UINT32, &mPosition, michael@0: DBUS_TYPE_UINT32, &tempPlayStatus, michael@0: DBUS_TYPE_INVALID); michael@0: NS_ENSURE_TRUE_VOID(success); michael@0: } michael@0: michael@0: private: michael@0: const nsString mDeviceAddress; michael@0: int32_t mDuration; michael@0: int32_t mPosition; michael@0: ControlPlayStatus mPlayStatus; michael@0: }; michael@0: michael@0: void michael@0: BluetoothDBusService::UpdatePlayStatus(uint32_t aDuration, michael@0: uint32_t aPosition, michael@0: ControlPlayStatus aPlayStatus) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: NS_ENSURE_TRUE_VOID(this->IsReady()); michael@0: michael@0: BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get(); michael@0: NS_ENSURE_TRUE_VOID(a2dp); michael@0: MOZ_ASSERT(a2dp->IsConnected()); michael@0: MOZ_ASSERT(a2dp->IsAvrcpConnected()); michael@0: MOZ_ASSERT(!sAdapterPath.IsEmpty()); michael@0: michael@0: nsAutoString deviceAddress; michael@0: a2dp->GetAddress(deviceAddress); michael@0: michael@0: Task* task = new UpdatePlayStatusTask(deviceAddress, michael@0: aDuration, michael@0: aPosition, michael@0: aPlayStatus); michael@0: DispatchToDBusThread(task); michael@0: } michael@0: michael@0: class UpdateNotificationTask : public Task michael@0: { michael@0: public: michael@0: UpdateNotificationTask(const nsAString& aDeviceAddress, michael@0: BluetoothDBusService::ControlEventId aEventId, michael@0: uint64_t aData) michael@0: : mDeviceAddress(aDeviceAddress) michael@0: , mEventId(aEventId) michael@0: , mData(aData) michael@0: { michael@0: MOZ_ASSERT(!mDeviceAddress.IsEmpty()); michael@0: } michael@0: michael@0: void Run() MOZ_OVERRIDE michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); // I/O thread michael@0: MOZ_ASSERT(sDBusConnection); michael@0: MOZ_ASSERT(!sAdapterPath.IsEmpty()); michael@0: michael@0: const nsCString objectPath = NS_ConvertUTF16toUTF8( michael@0: GetObjectPathFromAddress(sAdapterPath, mDeviceAddress)); michael@0: michael@0: uint16_t eventId = mEventId; michael@0: michael@0: bool success = sDBusConnection->SendWithReply( michael@0: ControlCallback, nullptr, -1, michael@0: BLUEZ_DBUS_BASE_IFC, michael@0: objectPath.get(), michael@0: DBUS_CTL_IFACE, "UpdateNotification", michael@0: DBUS_TYPE_UINT16, &eventId, michael@0: DBUS_TYPE_UINT64, &mData, michael@0: DBUS_TYPE_INVALID); michael@0: NS_ENSURE_TRUE_VOID(success); michael@0: } michael@0: michael@0: private: michael@0: const nsString mDeviceAddress; michael@0: int16_t mEventId; michael@0: int32_t mData; michael@0: }; michael@0: michael@0: void michael@0: BluetoothDBusService::UpdateNotification(ControlEventId aEventId, michael@0: uint64_t aData) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: NS_ENSURE_TRUE_VOID(this->IsReady()); michael@0: michael@0: BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get(); michael@0: NS_ENSURE_TRUE_VOID(a2dp); michael@0: MOZ_ASSERT(a2dp->IsConnected()); michael@0: MOZ_ASSERT(a2dp->IsAvrcpConnected()); michael@0: MOZ_ASSERT(!sAdapterPath.IsEmpty()); michael@0: michael@0: nsAutoString deviceAddress; michael@0: a2dp->GetAddress(deviceAddress); michael@0: michael@0: Task* task = new UpdateNotificationTask(deviceAddress, aEventId, aData); michael@0: DispatchToDBusThread(task); michael@0: }