1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/bluetooth/bluez/BluetoothDBusService.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,4192 @@ 1.4 +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ 1.5 +/* vim: set ts=2 et sw=2 tw=80: */ 1.6 +/* 1.7 +** Copyright 2006, The Android Open Source Project 1.8 +** 1.9 +** Licensed under the Apache License, Version 2.0 (the "License"); 1.10 +** you may not use this file except in compliance with the License. 1.11 +** You may obtain a copy of the License at 1.12 +** 1.13 +** http://www.apache.org/licenses/LICENSE-2.0 1.14 +** 1.15 +** Unless required by applicable law or agreed to in writing, software 1.16 +** distributed under the License is distributed on an "AS IS" BASIS, 1.17 +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1.18 +** See the License for the specific language governing permissions and 1.19 +** limitations under the License. 1.20 +*/ 1.21 + 1.22 +#include "base/basictypes.h" 1.23 +#include "BluetoothDBusService.h" 1.24 +#include "BluetoothA2dpManager.h" 1.25 +#include "BluetoothHfpManager.h" 1.26 +#include "BluetoothHidManager.h" 1.27 +#include "BluetoothOppManager.h" 1.28 +#include "BluetoothProfileController.h" 1.29 +#include "BluetoothReplyRunnable.h" 1.30 +#include "BluetoothUnixSocketConnector.h" 1.31 +#include "BluetoothUtils.h" 1.32 +#include "BluetoothUuid.h" 1.33 + 1.34 +#include <cstdio> 1.35 +#include <dbus/dbus.h> 1.36 + 1.37 +#include "nsAutoPtr.h" 1.38 +#include "nsThreadUtils.h" 1.39 +#include "nsDebug.h" 1.40 +#include "nsDataHashtable.h" 1.41 +#include "mozilla/ArrayUtils.h" 1.42 +#include "mozilla/Atomics.h" 1.43 +#include "mozilla/dom/bluetooth/BluetoothTypes.h" 1.44 +#include "mozilla/Hal.h" 1.45 +#include "mozilla/ipc/UnixSocket.h" 1.46 +#include "mozilla/ipc/DBusUtils.h" 1.47 +#include "mozilla/ipc/RawDBusConnection.h" 1.48 +#include "mozilla/LazyIdleThread.h" 1.49 +#include "mozilla/Mutex.h" 1.50 +#include "mozilla/NullPtr.h" 1.51 +#include "mozilla/StaticMutex.h" 1.52 +#include "mozilla/unused.h" 1.53 + 1.54 +#if defined(MOZ_WIDGET_GONK) 1.55 +#include "cutils/properties.h" 1.56 +#include <dlfcn.h> 1.57 +#endif 1.58 + 1.59 +/** 1.60 + * Some rules for dealing with memory in DBus: 1.61 + * - A DBusError only needs to be deleted if it's been set, not just 1.62 + * initialized. This is why LOG_AND_FREE... is called only when an error is 1.63 + * set, and the macro cleans up the error itself. 1.64 + * - A DBusMessage needs to be unrefed when it is newed explicitly. DBusMessages 1.65 + * from signals do not need to be unrefed, as they will be cleaned by DBus 1.66 + * after DBUS_HANDLER_RESULT_HANDLED is returned from the filter. 1.67 + */ 1.68 + 1.69 +using namespace mozilla; 1.70 +using namespace mozilla::ipc; 1.71 +USING_BLUETOOTH_NAMESPACE 1.72 + 1.73 +#define B2G_AGENT_CAPABILITIES "DisplayYesNo" 1.74 +#define DBUS_MANAGER_IFACE BLUEZ_DBUS_BASE_IFC ".Manager" 1.75 +#define DBUS_ADAPTER_IFACE BLUEZ_DBUS_BASE_IFC ".Adapter" 1.76 +#define DBUS_DEVICE_IFACE BLUEZ_DBUS_BASE_IFC ".Device" 1.77 +#define DBUS_AGENT_IFACE BLUEZ_DBUS_BASE_IFC ".Agent" 1.78 +#define DBUS_SINK_IFACE BLUEZ_DBUS_BASE_IFC ".AudioSink" 1.79 +#define DBUS_CTL_IFACE BLUEZ_DBUS_BASE_IFC ".Control" 1.80 +#define DBUS_INPUT_IFACE BLUEZ_DBUS_BASE_IFC ".Input" 1.81 +#define BLUEZ_DBUS_BASE_PATH "/org/bluez" 1.82 +#define BLUEZ_DBUS_BASE_IFC "org.bluez" 1.83 +#define BLUEZ_ERROR_IFC "org.bluez.Error" 1.84 + 1.85 +#define ERR_A2DP_IS_DISCONNECTED "A2dpIsDisconnected" 1.86 +#define ERR_AVRCP_IS_DISCONNECTED "AvrcpIsDisconnected" 1.87 + 1.88 +/** 1.89 + * To not lock Bluetooth switch button on Settings UI because of any accident, 1.90 + * we will force disabling Bluetooth 5 seconds after the user requesting to 1.91 + * turn off Bluetooth. 1.92 + */ 1.93 +#define TIMEOUT_FORCE_TO_DISABLE_BT 5 1.94 + 1.95 +#define BT_LAZY_THREAD_TIMEOUT_MS 3000 1.96 + 1.97 +#ifdef MOZ_WIDGET_GONK 1.98 +class Bluedroid 1.99 +{ 1.100 + struct ScopedDlHandleTraits 1.101 + { 1.102 + typedef void* type; 1.103 + static void* empty() 1.104 + { 1.105 + return nullptr; 1.106 + } 1.107 + static void release(void* handle) 1.108 + { 1.109 + if (!handle) { 1.110 + return; 1.111 + } 1.112 + int res = dlclose(handle); 1.113 + if (res) { 1.114 + BT_WARNING("Failed to close libbluedroid.so: %s", dlerror()); 1.115 + } 1.116 + } 1.117 + }; 1.118 + 1.119 +public: 1.120 + Bluedroid() 1.121 + : m_bt_enable(nullptr) 1.122 + , m_bt_disable(nullptr) 1.123 + , m_bt_is_enabled(nullptr) 1.124 + {} 1.125 + 1.126 + bool Enable() 1.127 + { 1.128 + MOZ_ASSERT(!NS_IsMainThread()); // BT thread 1.129 + 1.130 + if (!mHandle && !Init()) { 1.131 + return false; 1.132 + } else if (m_bt_is_enabled() == 1) { 1.133 + return true; 1.134 + } 1.135 + // 0 == success, -1 == error 1.136 + return !m_bt_enable(); 1.137 + } 1.138 + 1.139 + bool Disable() 1.140 + { 1.141 + MOZ_ASSERT(!NS_IsMainThread()); // BT thread 1.142 + 1.143 + if (!IsEnabled()) { 1.144 + return true; 1.145 + } 1.146 + // 0 == success, -1 == error 1.147 + return !m_bt_disable(); 1.148 + } 1.149 + 1.150 + bool IsEnabled() const 1.151 + { 1.152 + MOZ_ASSERT(!NS_IsMainThread()); // BT thread 1.153 + 1.154 + if (!mHandle) { 1.155 + return false; 1.156 + } 1.157 + // 1 == enabled, 0 == disabled, -1 == error 1.158 + return m_bt_is_enabled() > 0; 1.159 + } 1.160 + 1.161 +private: 1.162 + bool Init() 1.163 + { 1.164 + MOZ_ASSERT(!mHandle); 1.165 + 1.166 + Scoped<ScopedDlHandleTraits> handle(dlopen("libbluedroid.so", RTLD_LAZY)); 1.167 + if (!handle) { 1.168 + BT_WARNING("Failed to open libbluedroid.so: %s", dlerror()); 1.169 + return false; 1.170 + } 1.171 + int (*bt_enable)() = (int (*)())dlsym(handle, "bt_enable"); 1.172 + if (!bt_enable) { 1.173 + BT_WARNING("Failed to lookup bt_enable: %s", dlerror()); 1.174 + return false; 1.175 + } 1.176 + int (*bt_disable)() = (int (*)())dlsym(handle, "bt_disable"); 1.177 + if (!bt_disable) { 1.178 + BT_WARNING("Failed to lookup bt_disable: %s", dlerror()); 1.179 + return false; 1.180 + } 1.181 + int (*bt_is_enabled)() = (int (*)())dlsym(handle, "bt_is_enabled"); 1.182 + if (!bt_is_enabled) { 1.183 + BT_WARNING("Failed to lookup bt_is_enabled: %s", dlerror()); 1.184 + return false; 1.185 + } 1.186 + 1.187 + m_bt_enable = bt_enable; 1.188 + m_bt_disable = bt_disable; 1.189 + m_bt_is_enabled = bt_is_enabled; 1.190 + mHandle.reset(handle.forget()); 1.191 + 1.192 + return true; 1.193 + } 1.194 + 1.195 + Scoped<ScopedDlHandleTraits> mHandle; 1.196 + int (* m_bt_enable)(void); 1.197 + int (* m_bt_disable)(void); 1.198 + int (* m_bt_is_enabled)(void); 1.199 +}; 1.200 + 1.201 +// 1.202 +// BT-thread-only variables 1.203 +// 1.204 +// The variables below must only be accessed from within the BT thread. 1.205 +// 1.206 + 1.207 +static class Bluedroid sBluedroid; 1.208 +#endif 1.209 + 1.210 +// 1.211 +// Read-only constants 1.212 +// 1.213 +// The constants below are read-only and may be accessed from any 1.214 +// thread. Most of the contain DBus state or settings, so keep them 1.215 +// on the I/O thread if somehow possible. 1.216 +// 1.217 + 1.218 +typedef struct { 1.219 + const char* name; 1.220 + int type; 1.221 +} Properties; 1.222 + 1.223 +static const Properties sDeviceProperties[] = { 1.224 + {"Address", DBUS_TYPE_STRING}, 1.225 + {"Name", DBUS_TYPE_STRING}, 1.226 + {"Icon", DBUS_TYPE_STRING}, 1.227 + {"Class", DBUS_TYPE_UINT32}, 1.228 + {"UUIDs", DBUS_TYPE_ARRAY}, 1.229 + {"Paired", DBUS_TYPE_BOOLEAN}, 1.230 + {"Connected", DBUS_TYPE_BOOLEAN}, 1.231 + {"Trusted", DBUS_TYPE_BOOLEAN}, 1.232 + {"Blocked", DBUS_TYPE_BOOLEAN}, 1.233 + {"Alias", DBUS_TYPE_STRING}, 1.234 + {"Nodes", DBUS_TYPE_ARRAY}, 1.235 + {"Adapter", DBUS_TYPE_OBJECT_PATH}, 1.236 + {"LegacyPairing", DBUS_TYPE_BOOLEAN}, 1.237 + {"RSSI", DBUS_TYPE_INT16}, 1.238 + {"TX", DBUS_TYPE_UINT32}, 1.239 + {"Type", DBUS_TYPE_STRING}, 1.240 + {"Broadcaster", DBUS_TYPE_BOOLEAN}, 1.241 + {"Services", DBUS_TYPE_ARRAY} 1.242 +}; 1.243 + 1.244 +static const Properties sAdapterProperties[] = { 1.245 + {"Address", DBUS_TYPE_STRING}, 1.246 + {"Name", DBUS_TYPE_STRING}, 1.247 + {"Class", DBUS_TYPE_UINT32}, 1.248 + {"Powered", DBUS_TYPE_BOOLEAN}, 1.249 + {"Discoverable", DBUS_TYPE_BOOLEAN}, 1.250 + {"DiscoverableTimeout", DBUS_TYPE_UINT32}, 1.251 + {"Pairable", DBUS_TYPE_BOOLEAN}, 1.252 + {"PairableTimeout", DBUS_TYPE_UINT32}, 1.253 + {"Discovering", DBUS_TYPE_BOOLEAN}, 1.254 + {"Devices", DBUS_TYPE_ARRAY}, 1.255 + {"UUIDs", DBUS_TYPE_ARRAY}, 1.256 + {"Type", DBUS_TYPE_STRING} 1.257 +}; 1.258 + 1.259 +static const Properties sManagerProperties[] = { 1.260 + {"Adapters", DBUS_TYPE_ARRAY}, 1.261 +}; 1.262 + 1.263 +static const Properties sSinkProperties[] = { 1.264 + {"State", DBUS_TYPE_STRING}, 1.265 + {"Connected", DBUS_TYPE_BOOLEAN}, 1.266 + {"Playing", DBUS_TYPE_BOOLEAN} 1.267 +}; 1.268 + 1.269 +static const Properties sControlProperties[] = { 1.270 + {"Connected", DBUS_TYPE_BOOLEAN} 1.271 +}; 1.272 + 1.273 +static const Properties sInputProperties[] = { 1.274 + {"Connected", DBUS_TYPE_BOOLEAN} 1.275 +}; 1.276 + 1.277 +static const char* const sBluetoothDBusIfaces[] = { 1.278 + DBUS_MANAGER_IFACE, 1.279 + DBUS_ADAPTER_IFACE, 1.280 + DBUS_DEVICE_IFACE 1.281 +}; 1.282 + 1.283 +static const char* const sBluetoothDBusSignals[] = { 1.284 + "type='signal',interface='org.freedesktop.DBus'", 1.285 + "type='signal',interface='org.bluez.Adapter'", 1.286 + "type='signal',interface='org.bluez.Manager'", 1.287 + "type='signal',interface='org.bluez.Device'", 1.288 + "type='signal',interface='org.bluez.Input'", 1.289 + "type='signal',interface='org.bluez.Network'", 1.290 + "type='signal',interface='org.bluez.NetworkServer'", 1.291 + "type='signal',interface='org.bluez.HealthDevice'", 1.292 + "type='signal',interface='org.bluez.AudioSink'", 1.293 + "type='signal',interface='org.bluez.Control'" 1.294 +}; 1.295 + 1.296 +// Only A2DP and HID are authorized. 1.297 +static const BluetoothServiceClass sAuthorizedServiceClass[] = { 1.298 + BluetoothServiceClass::A2DP, 1.299 + BluetoothServiceClass::HID 1.300 +}; 1.301 + 1.302 +/** 1.303 + * The adapter name may not be ready whenever event 'AdapterAdded' is received, 1.304 + * so we'd like to wait for a bit. Only used on main thread. 1.305 + */ 1.306 +static const int sWaitingForAdapterNameInterval = 1000; // unit: ms 1.307 + 1.308 +// 1.309 +// main-thread-only variables 1.310 +// 1.311 +// The variables below must be accessed from within the main thread. 1.312 +// 1.313 + 1.314 +// A queue for connect/disconnect request. See Bug 913372 for details. 1.315 +static nsTArray<nsRefPtr<BluetoothProfileController> > sControllerArray; 1.316 + 1.317 +// 1.318 +// I/O-thread-only variables 1.319 +// 1.320 +// The variables below must be accessed from within the I/O thread. 1.321 +// 1.322 + 1.323 +// The DBus connection to the BlueZ daemon 1.324 +static StaticAutoPtr<RawDBusConnection> sDBusConnection; 1.325 + 1.326 +// Keep the pairing requests. 1.327 +static unsigned int sIsPairing = 0; 1.328 + 1.329 +static nsDataHashtable<nsStringHashKey, DBusMessage* >* sPairingReqTable; 1.330 + 1.331 +// The object path of the adapter that should 1.332 +// be updated after switching Bluetooth. 1.333 +static nsString sAdapterPath; 1.334 + 1.335 +// 1.336 +// The variables below are currently accessed from within multiple 1.337 +// threads and should be moved to one specific thread if possible. 1.338 +// 1.339 +// TODO: Concurrency control is implemented by locking. Maybe there's 1.340 +// a non-blocking way to implement this. 1.341 +// 1.342 + 1.343 +// Disconnect all profiles before turning off Bluetooth. Please see Bug 891257 1.344 +// for more details. |sStopBluetoothMonitor| protects access to this variable. 1.345 +static int sConnectedDeviceCount = 0; 1.346 +static StaticAutoPtr<Monitor> sStopBluetoothMonitor; 1.347 + 1.348 +// Protects against bug 969447. 1.349 +static StaticAutoPtr<Monitor> sGetPropertyMonitor; 1.350 + 1.351 +typedef void (*UnpackFunc)(DBusMessage*, DBusError*, BluetoothValue&, nsAString&); 1.352 +typedef bool (*FilterFunc)(const BluetoothValue&); 1.353 + 1.354 +static void 1.355 +DispatchToDBusThread(Task* task) 1.356 +{ 1.357 + XRE_GetIOMessageLoop()->PostTask(FROM_HERE, task); 1.358 +} 1.359 + 1.360 +static nsresult 1.361 +DispatchToBtThread(nsIRunnable* aRunnable) 1.362 +{ 1.363 + /* Due to the fact that the startup and shutdown of the Bluetooth 1.364 + * system can take an indefinite amount of time, a separate thread 1.365 + * is used for running blocking calls. The thread is not intended 1.366 + * for regular Bluetooth operations though. 1.367 + */ 1.368 + static StaticRefPtr<LazyIdleThread> sBluetoothThread; 1.369 + 1.370 + MOZ_ASSERT(NS_IsMainThread()); 1.371 + 1.372 + if (!sBluetoothThread) { 1.373 + sBluetoothThread = new LazyIdleThread(BT_LAZY_THREAD_TIMEOUT_MS, 1.374 + NS_LITERAL_CSTRING("BluetoothDBusService"), 1.375 + LazyIdleThread::ManualShutdown); 1.376 + } 1.377 + return sBluetoothThread->Dispatch(aRunnable, NS_DISPATCH_NORMAL); 1.378 +} 1.379 + 1.380 +BluetoothDBusService::BluetoothDBusService() 1.381 +{ 1.382 + sGetPropertyMonitor = new Monitor("BluetoothService.sGetPropertyMonitor"); 1.383 + sStopBluetoothMonitor = new Monitor("BluetoothService.sStopBluetoothMonitor"); 1.384 +} 1.385 + 1.386 +BluetoothDBusService::~BluetoothDBusService() 1.387 +{ 1.388 + sStopBluetoothMonitor = nullptr; 1.389 + sGetPropertyMonitor = nullptr; 1.390 +} 1.391 + 1.392 +static bool 1.393 +GetConnectedDevicesFilter(const BluetoothValue& aValue) 1.394 +{ 1.395 + // We don't have to filter device here 1.396 + return true; 1.397 +} 1.398 + 1.399 +static bool 1.400 +GetPairedDevicesFilter(const BluetoothValue& aValue) 1.401 +{ 1.402 + // Check property 'Paired' and only paired device will be returned 1.403 + if (aValue.type() != BluetoothValue::TArrayOfBluetoothNamedValue) { 1.404 + BT_WARNING("Not a BluetoothNamedValue array!"); 1.405 + return false; 1.406 + } 1.407 + 1.408 + const InfallibleTArray<BluetoothNamedValue>& deviceProperties = 1.409 + aValue.get_ArrayOfBluetoothNamedValue(); 1.410 + uint32_t length = deviceProperties.Length(); 1.411 + for (uint32_t p = 0; p < length; ++p) { 1.412 + if (deviceProperties[p].name().EqualsLiteral("Paired")) { 1.413 + return deviceProperties[p].value().get_bool(); 1.414 + } 1.415 + } 1.416 + 1.417 + return false; 1.418 +} 1.419 + 1.420 +class DistributeBluetoothSignalTask : public nsRunnable 1.421 +{ 1.422 +public: 1.423 + DistributeBluetoothSignalTask(const BluetoothSignal& aSignal) 1.424 + : mSignal(aSignal) 1.425 + { 1.426 + } 1.427 + 1.428 + nsresult Run() 1.429 + { 1.430 + MOZ_ASSERT(NS_IsMainThread()); 1.431 + 1.432 + BluetoothService* bs = BluetoothService::Get(); 1.433 + NS_ENSURE_TRUE(bs, NS_ERROR_FAILURE); 1.434 + bs->DistributeSignal(mSignal); 1.435 + 1.436 + return NS_OK; 1.437 + } 1.438 + 1.439 +private: 1.440 + BluetoothSignal mSignal; 1.441 +}; 1.442 + 1.443 +class ControlPropertyChangedHandler : public nsRunnable 1.444 +{ 1.445 +public: 1.446 + ControlPropertyChangedHandler(const BluetoothSignal& aSignal) 1.447 + : mSignal(aSignal) 1.448 + { 1.449 + } 1.450 + 1.451 + nsresult Run() 1.452 + { 1.453 + MOZ_ASSERT(NS_IsMainThread()); 1.454 + if (mSignal.value().type() != BluetoothValue::TArrayOfBluetoothNamedValue) { 1.455 + BT_WARNING("Wrong value type for ControlPropertyChangedHandler"); 1.456 + return NS_ERROR_FAILURE; 1.457 + } 1.458 + 1.459 + InfallibleTArray<BluetoothNamedValue>& arr = 1.460 + mSignal.value().get_ArrayOfBluetoothNamedValue(); 1.461 + MOZ_ASSERT(arr[0].name().EqualsLiteral("Connected")); 1.462 + MOZ_ASSERT(arr[0].value().type() == BluetoothValue::Tbool); 1.463 + bool connected = arr[0].value().get_bool(); 1.464 + 1.465 + BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get(); 1.466 + NS_ENSURE_TRUE(a2dp, NS_ERROR_FAILURE); 1.467 + a2dp->SetAvrcpConnected(connected); 1.468 + return NS_OK; 1.469 + } 1.470 + 1.471 +private: 1.472 + BluetoothSignal mSignal; 1.473 +}; 1.474 + 1.475 +class SinkPropertyChangedHandler : public nsRunnable 1.476 +{ 1.477 +public: 1.478 + SinkPropertyChangedHandler(const BluetoothSignal& aSignal) 1.479 + : mSignal(aSignal) 1.480 + { 1.481 + } 1.482 + 1.483 + NS_IMETHOD 1.484 + Run() 1.485 + { 1.486 + MOZ_ASSERT(NS_IsMainThread()); 1.487 + MOZ_ASSERT(mSignal.name().EqualsLiteral("PropertyChanged")); 1.488 + MOZ_ASSERT(mSignal.value().type() == 1.489 + BluetoothValue::TArrayOfBluetoothNamedValue); 1.490 + 1.491 + // Replace object path with device address 1.492 + nsString address = GetAddressFromObjectPath(mSignal.path()); 1.493 + mSignal.path() = address; 1.494 + 1.495 + BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get(); 1.496 + NS_ENSURE_TRUE(a2dp, NS_ERROR_FAILURE); 1.497 + a2dp->HandleSinkPropertyChanged(mSignal); 1.498 + return NS_OK; 1.499 + } 1.500 + 1.501 +private: 1.502 + BluetoothSignal mSignal; 1.503 +}; 1.504 + 1.505 +class InputPropertyChangedHandler : public nsRunnable 1.506 +{ 1.507 +public: 1.508 + InputPropertyChangedHandler(const BluetoothSignal& aSignal) 1.509 + : mSignal(aSignal) 1.510 + { 1.511 + } 1.512 + 1.513 + NS_IMETHOD 1.514 + Run() 1.515 + { 1.516 + MOZ_ASSERT(NS_IsMainThread()); 1.517 + MOZ_ASSERT(mSignal.name().EqualsLiteral("PropertyChanged")); 1.518 + MOZ_ASSERT(mSignal.value().type() == BluetoothValue::TArrayOfBluetoothNamedValue); 1.519 + 1.520 + // Replace object path with device address 1.521 + nsString address = GetAddressFromObjectPath(mSignal.path()); 1.522 + mSignal.path() = address; 1.523 + 1.524 + BluetoothHidManager* hid = BluetoothHidManager::Get(); 1.525 + NS_ENSURE_TRUE(hid, NS_ERROR_FAILURE); 1.526 + hid->HandleInputPropertyChanged(mSignal); 1.527 + return NS_OK; 1.528 + } 1.529 + 1.530 +private: 1.531 + BluetoothSignal mSignal; 1.532 +}; 1.533 + 1.534 +class TryFiringAdapterAddedTask : public Task 1.535 +{ 1.536 +public: 1.537 + void Run() MOZ_OVERRIDE 1.538 + { 1.539 + MOZ_ASSERT(NS_IsMainThread()); 1.540 + 1.541 + BluetoothService* bs = BluetoothService::Get(); 1.542 + NS_ENSURE_TRUE_VOID(bs); 1.543 + 1.544 + bs->AdapterAddedReceived(); 1.545 + bs->TryFiringAdapterAdded(); 1.546 + } 1.547 +}; 1.548 + 1.549 +class TryFiringAdapterAddedRunnable : public nsRunnable 1.550 +{ 1.551 +public: 1.552 + TryFiringAdapterAddedRunnable(bool aDelay) 1.553 + : mDelay(aDelay) 1.554 + { } 1.555 + 1.556 + nsresult Run() 1.557 + { 1.558 + MOZ_ASSERT(NS_IsMainThread()); 1.559 + 1.560 + if (mDelay) { 1.561 + MessageLoop::current()-> 1.562 + PostDelayedTask(FROM_HERE, new TryFiringAdapterAddedTask(), 1.563 + sWaitingForAdapterNameInterval); 1.564 + } else { 1.565 + MessageLoop::current()-> 1.566 + PostTask(FROM_HERE, new TryFiringAdapterAddedTask()); 1.567 + } 1.568 + 1.569 + return NS_OK; 1.570 + } 1.571 + 1.572 +private: 1.573 + bool mDelay; 1.574 +}; 1.575 + 1.576 +static bool 1.577 +IsDBusMessageError(DBusMessage* aMsg, DBusError* aErr, nsAString& aErrorStr) 1.578 +{ 1.579 + if (aErr && dbus_error_is_set(aErr)) { 1.580 + aErrorStr = NS_ConvertUTF8toUTF16(aErr->message); 1.581 + LOG_AND_FREE_DBUS_ERROR(aErr); 1.582 + return true; 1.583 + } 1.584 + 1.585 + DBusError err; 1.586 + dbus_error_init(&err); 1.587 + if (dbus_message_get_type(aMsg) == DBUS_MESSAGE_TYPE_ERROR) { 1.588 + const char* error_msg; 1.589 + if (!dbus_message_get_args(aMsg, &err, DBUS_TYPE_STRING, 1.590 + &error_msg, DBUS_TYPE_INVALID) || 1.591 + !error_msg) { 1.592 + if (dbus_error_is_set(&err)) { 1.593 + aErrorStr = NS_ConvertUTF8toUTF16(err.message); 1.594 + LOG_AND_FREE_DBUS_ERROR(&err); 1.595 + return true; 1.596 + } else { 1.597 + aErrorStr.AssignLiteral("Unknown Error"); 1.598 + return true; 1.599 + } 1.600 + } else { 1.601 + aErrorStr = NS_ConvertUTF8toUTF16(error_msg); 1.602 + return true; 1.603 + } 1.604 + } 1.605 + return false; 1.606 +} 1.607 + 1.608 +static void 1.609 +UnpackObjectPathMessage(DBusMessage* aMsg, DBusError* aErr, 1.610 + BluetoothValue& aValue, nsAString& aErrorStr) 1.611 +{ 1.612 + DBusError err; 1.613 + dbus_error_init(&err); 1.614 + if (!IsDBusMessageError(aMsg, aErr, aErrorStr)) { 1.615 + MOZ_ASSERT(dbus_message_get_type(aMsg) == DBUS_MESSAGE_TYPE_METHOD_RETURN, 1.616 + "Got dbus callback that's not a METHOD_RETURN!"); 1.617 + const char* object_path; 1.618 + if (!dbus_message_get_args(aMsg, &err, DBUS_TYPE_OBJECT_PATH, 1.619 + &object_path, DBUS_TYPE_INVALID) || 1.620 + !object_path) { 1.621 + if (dbus_error_is_set(&err)) { 1.622 + aErrorStr = NS_ConvertUTF8toUTF16(err.message); 1.623 + LOG_AND_FREE_DBUS_ERROR(&err); 1.624 + } 1.625 + } else { 1.626 + aValue = NS_ConvertUTF8toUTF16(object_path); 1.627 + } 1.628 + } 1.629 +} 1.630 + 1.631 +class PrepareProfileManagersRunnable : public nsRunnable 1.632 +{ 1.633 +public: 1.634 + nsresult Run() 1.635 + { 1.636 + BluetoothHfpManager* hfp = BluetoothHfpManager::Get(); 1.637 + if (!hfp || !hfp->Listen()) { 1.638 + BT_WARNING("Failed to start listening for BluetoothHfpManager!"); 1.639 + return NS_ERROR_FAILURE; 1.640 + } 1.641 + 1.642 + BluetoothOppManager* opp = BluetoothOppManager::Get(); 1.643 + if (!opp || !opp->Listen()) { 1.644 + BT_WARNING("Failed to start listening for BluetoothOppManager!"); 1.645 + return NS_ERROR_FAILURE; 1.646 + } 1.647 + 1.648 + BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get(); 1.649 + NS_ENSURE_TRUE(a2dp, NS_ERROR_FAILURE); 1.650 + a2dp->Reset(); 1.651 + 1.652 + return NS_OK; 1.653 + } 1.654 +}; 1.655 + 1.656 +static void 1.657 +RunDBusCallback(DBusMessage* aMsg, void* aBluetoothReplyRunnable, 1.658 + UnpackFunc aFunc) 1.659 +{ 1.660 +#ifdef MOZ_WIDGET_GONK 1.661 + // Due to the fact that we're running two dbus loops on desktop implicitly by 1.662 + // being gtk based, sometimes we'll get signals/reply coming in on the main 1.663 + // thread. There's not a lot we can do about that for the time being and it 1.664 + // (technically) shouldn't hurt anything. However, on gonk, die. 1.665 + MOZ_ASSERT(!NS_IsMainThread()); // I/O thread 1.666 +#endif 1.667 + nsRefPtr<BluetoothReplyRunnable> replyRunnable = 1.668 + dont_AddRef(static_cast< BluetoothReplyRunnable* >(aBluetoothReplyRunnable)); 1.669 + 1.670 + MOZ_ASSERT(replyRunnable, "Callback reply runnable is null!"); 1.671 + 1.672 + nsAutoString replyError; 1.673 + BluetoothValue v; 1.674 + aFunc(aMsg, nullptr, v, replyError); 1.675 + 1.676 + // Bug 941462. When blueZ replys 'I/O error', we treat it as 'internal error'. 1.677 + // This usually happned when the first pairing request has not yet finished, 1.678 + // the second pairing request issued immediately. 1.679 + if (replyError.EqualsLiteral("I/O error")) { 1.680 + replyError.AssignLiteral(ERR_INTERNAL_ERROR); 1.681 + } 1.682 + 1.683 + DispatchBluetoothReply(replyRunnable, v, replyError); 1.684 +} 1.685 + 1.686 +static void 1.687 +GetObjectPathCallback(DBusMessage* aMsg, void* aBluetoothReplyRunnable) 1.688 +{ 1.689 + if (sIsPairing) { 1.690 + RunDBusCallback(aMsg, aBluetoothReplyRunnable, 1.691 + UnpackObjectPathMessage); 1.692 + sIsPairing--; 1.693 + } 1.694 +} 1.695 + 1.696 +static void 1.697 +UnpackVoidMessage(DBusMessage* aMsg, DBusError* aErr, BluetoothValue& aValue, 1.698 + nsAString& aErrorStr) 1.699 +{ 1.700 + DBusError err; 1.701 + dbus_error_init(&err); 1.702 + if (!IsDBusMessageError(aMsg, aErr, aErrorStr) && 1.703 + dbus_message_get_type(aMsg) == DBUS_MESSAGE_TYPE_METHOD_RETURN && 1.704 + !dbus_message_get_args(aMsg, &err, DBUS_TYPE_INVALID)) { 1.705 + if (dbus_error_is_set(&err)) { 1.706 + aErrorStr = NS_ConvertUTF8toUTF16(err.message); 1.707 + LOG_AND_FREE_DBUS_ERROR(&err); 1.708 + } 1.709 + } 1.710 + aValue = aErrorStr.IsEmpty(); 1.711 +} 1.712 + 1.713 +static void 1.714 +GetVoidCallback(DBusMessage* aMsg, void* aBluetoothReplyRunnable) 1.715 +{ 1.716 + RunDBusCallback(aMsg, aBluetoothReplyRunnable, 1.717 + UnpackVoidMessage); 1.718 +} 1.719 + 1.720 +class ReplyErrorToProfileManager : public nsRunnable 1.721 +{ 1.722 +public: 1.723 + ReplyErrorToProfileManager(BluetoothServiceClass aServiceClass, 1.724 + bool aConnect, 1.725 + const nsAString& aErrorString) 1.726 + : mServiceClass(aServiceClass) 1.727 + , mConnect(aConnect) 1.728 + , mErrorString(aErrorString) 1.729 + { 1.730 + MOZ_ASSERT(!NS_IsMainThread()); // I/O thread 1.731 + } 1.732 + 1.733 + nsresult Run() 1.734 + { 1.735 + MOZ_ASSERT(NS_IsMainThread()); 1.736 + 1.737 + BluetoothProfileManagerBase* profile; 1.738 + if (mServiceClass == BluetoothServiceClass::HID) { 1.739 + profile = BluetoothHidManager::Get(); 1.740 + } else if (mServiceClass == BluetoothServiceClass::A2DP) { 1.741 + profile = BluetoothA2dpManager::Get(); 1.742 + } else { 1.743 + MOZ_ASSERT(false); 1.744 + return NS_ERROR_FAILURE; 1.745 + } 1.746 + 1.747 + if (mConnect) { 1.748 + profile->OnConnect(mErrorString); 1.749 + } else { 1.750 + profile->OnDisconnect(mErrorString); 1.751 + } 1.752 + 1.753 + return NS_OK; 1.754 + } 1.755 + 1.756 +private: 1.757 + BluetoothServiceClass mServiceClass; 1.758 + bool mConnect; 1.759 + nsString mErrorString; 1.760 +}; 1.761 + 1.762 +static void 1.763 +CheckDBusReply(DBusMessage* aMsg, void* aServiceClass, bool aConnect) 1.764 +{ 1.765 + MOZ_ASSERT(!NS_IsMainThread()); // I/O thread 1.766 + 1.767 + NS_ENSURE_TRUE_VOID(aMsg); 1.768 + 1.769 + BluetoothValue v; 1.770 + nsAutoString replyError; 1.771 + UnpackVoidMessage(aMsg, nullptr, v, replyError); 1.772 + 1.773 + nsAutoPtr<BluetoothServiceClass> serviceClass( 1.774 + static_cast<BluetoothServiceClass*>(aServiceClass)); 1.775 + 1.776 + if (!replyError.IsEmpty()) { 1.777 + NS_DispatchToMainThread( 1.778 + new ReplyErrorToProfileManager(*serviceClass, aConnect, replyError)); 1.779 + } 1.780 +} 1.781 + 1.782 +static void 1.783 +InputConnectCallback(DBusMessage* aMsg, void* aParam) 1.784 +{ 1.785 + CheckDBusReply(aMsg, aParam, true); 1.786 +} 1.787 + 1.788 +static void 1.789 +InputDisconnectCallback(DBusMessage* aMsg, void* aParam) 1.790 +{ 1.791 + CheckDBusReply(aMsg, aParam, false); 1.792 +} 1.793 + 1.794 +static void 1.795 +SinkConnectCallback(DBusMessage* aMsg, void* aParam) 1.796 +{ 1.797 + CheckDBusReply(aMsg, aParam, true); 1.798 +} 1.799 + 1.800 +static void 1.801 +SinkDisconnectCallback(DBusMessage* aMsg, void* aParam) 1.802 +{ 1.803 + CheckDBusReply(aMsg, aParam, false); 1.804 +} 1.805 + 1.806 +static bool 1.807 +HasAudioService(uint32_t aCodValue) 1.808 +{ 1.809 + return ((aCodValue & 0x200000) == 0x200000); 1.810 +} 1.811 + 1.812 +static bool 1.813 +ContainsIcon(const InfallibleTArray<BluetoothNamedValue>& aProperties) 1.814 +{ 1.815 + for (uint8_t i = 0; i < aProperties.Length(); i++) { 1.816 + if (aProperties[i].name().EqualsLiteral("Icon")) { 1.817 + return true; 1.818 + } 1.819 + } 1.820 + return false; 1.821 +} 1.822 + 1.823 +static bool 1.824 +GetProperty(DBusMessageIter aIter, const Properties* aPropertyTypes, 1.825 + int aPropertyTypeLen, int* aPropIndex, 1.826 + InfallibleTArray<BluetoothNamedValue>& aProperties) 1.827 +{ 1.828 + /** 1.829 + * Ensure GetProperty runs in critical section otherwise 1.830 + * crash due to timing issue occurs when BT is enabled. 1.831 + * 1.832 + * TODO: Revise GetProperty to solve the crash 1.833 + */ 1.834 + MonitorAutoLock lock(*sGetPropertyMonitor); 1.835 + 1.836 + DBusMessageIter prop_val, array_val_iter; 1.837 + char* property = nullptr; 1.838 + uint32_t array_type; 1.839 + int i, expectedType, receivedType; 1.840 + 1.841 + if (dbus_message_iter_get_arg_type(&aIter) != DBUS_TYPE_STRING) { 1.842 + return false; 1.843 + } 1.844 + 1.845 + dbus_message_iter_get_basic(&aIter, &property); 1.846 + 1.847 + if (!dbus_message_iter_next(&aIter) || 1.848 + dbus_message_iter_get_arg_type(&aIter) != DBUS_TYPE_VARIANT) { 1.849 + return false; 1.850 + } 1.851 + 1.852 + for (i = 0; i < aPropertyTypeLen; i++) { 1.853 + if (!strncmp(property, aPropertyTypes[i].name, strlen(property))) { 1.854 + break; 1.855 + } 1.856 + } 1.857 + 1.858 + if (i == aPropertyTypeLen) { 1.859 + BT_LOGR("unknown property: %s", property); 1.860 + return false; 1.861 + } 1.862 + 1.863 + nsAutoString propertyName; 1.864 + propertyName.AssignASCII(aPropertyTypes[i].name); 1.865 + *aPropIndex = i; 1.866 + 1.867 + // Preprocessing 1.868 + dbus_message_iter_recurse(&aIter, &prop_val); 1.869 + expectedType = aPropertyTypes[*aPropIndex].type; 1.870 + receivedType = dbus_message_iter_get_arg_type(&prop_val); 1.871 + 1.872 + /** 1.873 + * Bug 857896. Since device property "Connected" could be a boolean value or 1.874 + * an 2-byte array, we need to check the value type here and convert the 1.875 + * first byte into a boolean manually. 1.876 + */ 1.877 + bool convert = false; 1.878 + if (propertyName.EqualsLiteral("Connected") && 1.879 + receivedType == DBUS_TYPE_ARRAY) { 1.880 + MOZ_ASSERT(aPropertyTypes == sDeviceProperties); 1.881 + convert = true; 1.882 + } 1.883 + 1.884 + if ((receivedType != expectedType) && !convert) { 1.885 + BT_WARNING("Iterator not type we expect! Property name: %s," 1.886 + "Property Type Expected: %d, Property Type Received: %d", 1.887 + NS_ConvertUTF16toUTF8(propertyName).get(), expectedType, receivedType); 1.888 + return false; 1.889 + } 1.890 + 1.891 + // Extract data 1.892 + BluetoothValue propertyValue; 1.893 + switch (receivedType) { 1.894 + case DBUS_TYPE_STRING: 1.895 + case DBUS_TYPE_OBJECT_PATH: 1.896 + const char* c; 1.897 + dbus_message_iter_get_basic(&prop_val, &c); 1.898 + propertyValue = NS_ConvertUTF8toUTF16(c); 1.899 + break; 1.900 + case DBUS_TYPE_UINT32: 1.901 + case DBUS_TYPE_INT16: 1.902 + uint32_t i; 1.903 + dbus_message_iter_get_basic(&prop_val, &i); 1.904 + propertyValue = i; 1.905 + break; 1.906 + case DBUS_TYPE_BOOLEAN: 1.907 + bool b; 1.908 + dbus_message_iter_get_basic(&prop_val, &b); 1.909 + propertyValue = b; 1.910 + break; 1.911 + case DBUS_TYPE_ARRAY: 1.912 + dbus_message_iter_recurse(&prop_val, &array_val_iter); 1.913 + array_type = dbus_message_iter_get_arg_type(&array_val_iter); 1.914 + if (array_type == DBUS_TYPE_OBJECT_PATH || 1.915 + array_type == DBUS_TYPE_STRING) { 1.916 + InfallibleTArray<nsString> arr; 1.917 + do { 1.918 + const char* tmp; 1.919 + dbus_message_iter_get_basic(&array_val_iter, &tmp); 1.920 + nsAutoString s; 1.921 + s = NS_ConvertUTF8toUTF16(tmp); 1.922 + arr.AppendElement(s); 1.923 + } while (dbus_message_iter_next(&array_val_iter)); 1.924 + propertyValue = arr; 1.925 + } else if (array_type == DBUS_TYPE_BYTE) { 1.926 + InfallibleTArray<uint8_t> arr; 1.927 + do { 1.928 + uint8_t tmp; 1.929 + dbus_message_iter_get_basic(&array_val_iter, &tmp); 1.930 + arr.AppendElement(tmp); 1.931 + } while (dbus_message_iter_next(&array_val_iter)); 1.932 + propertyValue = arr; 1.933 + } else { 1.934 + // This happens when the array is 0-length. Apparently we get a 1.935 + // DBUS_TYPE_INVALID type. 1.936 + propertyValue = InfallibleTArray<nsString>(); 1.937 + } 1.938 + break; 1.939 + default: 1.940 + NS_NOTREACHED("Cannot find dbus message type!"); 1.941 + } 1.942 + 1.943 + // Postprocessing 1.944 + if (convert) { 1.945 + MOZ_ASSERT(propertyValue.type() == BluetoothValue::TArrayOfuint8_t); 1.946 + 1.947 + bool b = propertyValue.get_ArrayOfuint8_t()[0]; 1.948 + propertyValue = BluetoothValue(b); 1.949 + } else if (propertyName.EqualsLiteral("Devices")) { 1.950 + MOZ_ASSERT(aPropertyTypes == sAdapterProperties); 1.951 + MOZ_ASSERT(propertyValue.type() == BluetoothValue::TArrayOfnsString); 1.952 + 1.953 + uint32_t length = propertyValue.get_ArrayOfnsString().Length(); 1.954 + for (uint32_t i= 0; i < length; i++) { 1.955 + nsString& data = propertyValue.get_ArrayOfnsString()[i]; 1.956 + data = GetAddressFromObjectPath(data); 1.957 + } 1.958 + } 1.959 + 1.960 + aProperties.AppendElement(BluetoothNamedValue(propertyName, propertyValue)); 1.961 + return true; 1.962 +} 1.963 + 1.964 +static void 1.965 +ParseProperties(DBusMessageIter* aIter, 1.966 + BluetoothValue& aValue, 1.967 + nsAString& aErrorStr, 1.968 + const Properties* aPropertyTypes, 1.969 + const int aPropertyTypeLen) 1.970 +{ 1.971 + DBusMessageIter dict_entry, dict; 1.972 + int prop_index = -1; 1.973 + 1.974 + MOZ_ASSERT(dbus_message_iter_get_arg_type(aIter) == DBUS_TYPE_ARRAY, 1.975 + "Trying to parse a property from sth. that's not an array"); 1.976 + 1.977 + dbus_message_iter_recurse(aIter, &dict); 1.978 + InfallibleTArray<BluetoothNamedValue> props; 1.979 + do { 1.980 + MOZ_ASSERT(dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY, 1.981 + "Trying to parse a property from sth. that's not an dict!"); 1.982 + dbus_message_iter_recurse(&dict, &dict_entry); 1.983 + 1.984 + if (!GetProperty(dict_entry, aPropertyTypes, aPropertyTypeLen, &prop_index, 1.985 + props)) { 1.986 + aErrorStr.AssignLiteral("Can't Create Property!"); 1.987 + BT_WARNING("Can't create property!"); 1.988 + return; 1.989 + } 1.990 + } while (dbus_message_iter_next(&dict)); 1.991 + 1.992 + aValue = props; 1.993 +} 1.994 + 1.995 +static bool 1.996 +UnpackPropertiesMessage(DBusMessage* aMsg, DBusError* aErr, 1.997 + BluetoothValue& aValue, const char* aIface) 1.998 +{ 1.999 + MOZ_ASSERT(aMsg); 1.1000 + 1.1001 + const Properties* propertyTypes; 1.1002 + int propertyTypesLength; 1.1003 + 1.1004 + nsAutoString errorStr; 1.1005 + if (IsDBusMessageError(aMsg, aErr, errorStr) || 1.1006 + dbus_message_get_type(aMsg) != DBUS_MESSAGE_TYPE_METHOD_RETURN) { 1.1007 + BT_WARNING("dbus message has an error."); 1.1008 + return false; 1.1009 + } 1.1010 + 1.1011 + DBusMessageIter iter; 1.1012 + if (!dbus_message_iter_init(aMsg, &iter)) { 1.1013 + BT_WARNING("Cannot create dbus message iter!"); 1.1014 + return false; 1.1015 + } 1.1016 + 1.1017 + if (!strcmp(aIface, DBUS_DEVICE_IFACE)) { 1.1018 + propertyTypes = sDeviceProperties; 1.1019 + propertyTypesLength = ArrayLength(sDeviceProperties); 1.1020 + } else if (!strcmp(aIface, DBUS_ADAPTER_IFACE)) { 1.1021 + propertyTypes = sAdapterProperties; 1.1022 + propertyTypesLength = ArrayLength(sAdapterProperties); 1.1023 + } else if (!strcmp(aIface, DBUS_MANAGER_IFACE)) { 1.1024 + propertyTypes = sManagerProperties; 1.1025 + propertyTypesLength = ArrayLength(sManagerProperties); 1.1026 + } else { 1.1027 + return false; 1.1028 + } 1.1029 + 1.1030 + ParseProperties(&iter, aValue, errorStr, propertyTypes, 1.1031 + propertyTypesLength); 1.1032 + return true; 1.1033 +} 1.1034 + 1.1035 +static void 1.1036 +ParsePropertyChange(DBusMessage* aMsg, BluetoothValue& aValue, 1.1037 + nsAString& aErrorStr, const Properties* aPropertyTypes, 1.1038 + const int aPropertyTypeLen) 1.1039 +{ 1.1040 + DBusMessageIter iter; 1.1041 + DBusError err; 1.1042 + int prop_index = -1; 1.1043 + InfallibleTArray<BluetoothNamedValue> props; 1.1044 + 1.1045 + dbus_error_init(&err); 1.1046 + if (!dbus_message_iter_init(aMsg, &iter)) { 1.1047 + BT_WARNING("Can't create iterator!"); 1.1048 + return; 1.1049 + } 1.1050 + 1.1051 + if (!GetProperty(iter, aPropertyTypes, aPropertyTypeLen, 1.1052 + &prop_index, props)) { 1.1053 + BT_WARNING("Can't get property!"); 1.1054 + aErrorStr.AssignLiteral("Can't get property!"); 1.1055 + return; 1.1056 + } 1.1057 + aValue = props; 1.1058 +} 1.1059 + 1.1060 +class AppendDeviceNameReplyHandler: public DBusReplyHandler 1.1061 +{ 1.1062 +public: 1.1063 + AppendDeviceNameReplyHandler(const nsCString& aIface, 1.1064 + const nsString& aDevicePath, 1.1065 + const BluetoothSignal& aSignal) 1.1066 + : mIface(aIface) 1.1067 + , mDevicePath(aDevicePath) 1.1068 + , mSignal(aSignal) 1.1069 + { 1.1070 + MOZ_ASSERT(!mIface.IsEmpty()); 1.1071 + MOZ_ASSERT(!mDevicePath.IsEmpty()); 1.1072 + } 1.1073 + 1.1074 + void Handle(DBusMessage* aReply) MOZ_OVERRIDE 1.1075 + { 1.1076 + MOZ_ASSERT(!NS_IsMainThread()); // I/O thread 1.1077 + 1.1078 + if (!aReply || (dbus_message_get_type(aReply) == DBUS_MESSAGE_TYPE_ERROR)) { 1.1079 + return; 1.1080 + } 1.1081 + 1.1082 + // Get device properties from result of GetProperties 1.1083 + 1.1084 + DBusError err; 1.1085 + dbus_error_init(&err); 1.1086 + 1.1087 + BluetoothValue deviceProperties; 1.1088 + 1.1089 + bool success = UnpackPropertiesMessage(aReply, &err, deviceProperties, 1.1090 + mIface.get()); 1.1091 + if (!success) { 1.1092 + BT_WARNING("Failed to get device properties"); 1.1093 + return; 1.1094 + } 1.1095 + 1.1096 + // First we replace object path with device address. 1.1097 + 1.1098 + InfallibleTArray<BluetoothNamedValue>& parameters = 1.1099 + mSignal.value().get_ArrayOfBluetoothNamedValue(); 1.1100 + nsString address = 1.1101 + GetAddressFromObjectPath(mDevicePath); 1.1102 + parameters[0].name().AssignLiteral("address"); 1.1103 + parameters[0].value() = address; 1.1104 + 1.1105 + // Then we append the device's name to the original signal's data. 1.1106 + 1.1107 + InfallibleTArray<BluetoothNamedValue>& properties = 1.1108 + deviceProperties.get_ArrayOfBluetoothNamedValue(); 1.1109 + uint32_t i; 1.1110 + for (i = 0; i < properties.Length(); i++) { 1.1111 + if (properties[i].name().EqualsLiteral("Name")) { 1.1112 + properties[i].name().AssignLiteral("name"); 1.1113 + parameters.AppendElement(properties[i]); 1.1114 + break; 1.1115 + } 1.1116 + } 1.1117 + MOZ_ASSERT_IF(i == properties.Length(), "failed to get device name"); 1.1118 + 1.1119 + nsRefPtr<DistributeBluetoothSignalTask> task = 1.1120 + new DistributeBluetoothSignalTask(mSignal); 1.1121 + NS_DispatchToMainThread(task); 1.1122 + } 1.1123 + 1.1124 +private: 1.1125 + nsCString mIface; 1.1126 + nsString mDevicePath; 1.1127 + BluetoothSignal mSignal; 1.1128 +}; 1.1129 + 1.1130 +static void 1.1131 +AppendDeviceName(BluetoothSignal& aSignal) 1.1132 +{ 1.1133 + MOZ_ASSERT(!NS_IsMainThread()); // I/O thread 1.1134 + MOZ_ASSERT(sDBusConnection); 1.1135 + 1.1136 + BluetoothValue v = aSignal.value(); 1.1137 + if (v.type() != BluetoothValue::TArrayOfBluetoothNamedValue || 1.1138 + v.get_ArrayOfBluetoothNamedValue().Length() == 0) { 1.1139 + BT_WARNING("Invalid argument type for AppendDeviceNameRunnable"); 1.1140 + return; 1.1141 + } 1.1142 + const InfallibleTArray<BluetoothNamedValue>& arr = 1.1143 + v.get_ArrayOfBluetoothNamedValue(); 1.1144 + 1.1145 + // Device object path should be put in the first element 1.1146 + if (!arr[0].name().EqualsLiteral("path") || 1.1147 + arr[0].value().type() != BluetoothValue::TnsString) { 1.1148 + BT_WARNING("Invalid object path for AppendDeviceNameRunnable"); 1.1149 + return; 1.1150 + } 1.1151 + 1.1152 + nsString devicePath = arr[0].value().get_nsString(); 1.1153 + 1.1154 + nsRefPtr<AppendDeviceNameReplyHandler> handler = 1.1155 + new AppendDeviceNameReplyHandler(nsCString(DBUS_DEVICE_IFACE), 1.1156 + devicePath, aSignal); 1.1157 + 1.1158 + bool success = sDBusConnection->SendWithReply( 1.1159 + AppendDeviceNameReplyHandler::Callback, handler.get(), 1000, 1.1160 + BLUEZ_DBUS_BASE_IFC, NS_ConvertUTF16toUTF8(devicePath).get(), 1.1161 + DBUS_DEVICE_IFACE, "GetProperties", DBUS_TYPE_INVALID); 1.1162 + 1.1163 + NS_ENSURE_TRUE_VOID(success); 1.1164 + 1.1165 + unused << handler.forget(); // picked up by callback handler 1.1166 +} 1.1167 + 1.1168 +class SetPairingConfirmationTask : public Task 1.1169 +{ 1.1170 +public: 1.1171 + SetPairingConfirmationTask(const nsAString& aDeviceAddress, 1.1172 + bool aConfirm, 1.1173 + BluetoothReplyRunnable* aRunnable) 1.1174 + : mDeviceAddress(aDeviceAddress) 1.1175 + , mConfirm(aConfirm) 1.1176 + , mRunnable(aRunnable) 1.1177 + { 1.1178 + MOZ_ASSERT(!mDeviceAddress.IsEmpty()); 1.1179 + } 1.1180 + 1.1181 + void Run() MOZ_OVERRIDE 1.1182 + { 1.1183 + MOZ_ASSERT(!NS_IsMainThread()); // I/O thread 1.1184 + MOZ_ASSERT(sDBusConnection); 1.1185 + 1.1186 + nsAutoString errorStr; 1.1187 + BluetoothValue v = true; 1.1188 + DBusMessage *msg; 1.1189 + 1.1190 + if (!sPairingReqTable->Get(mDeviceAddress, &msg) && mRunnable) { 1.1191 + BT_WARNING("%s: Couldn't get original request message.", __FUNCTION__); 1.1192 + errorStr.AssignLiteral("Couldn't get original request message."); 1.1193 + DispatchBluetoothReply(mRunnable, v, errorStr); 1.1194 + 1.1195 + return; 1.1196 + } 1.1197 + 1.1198 + DBusMessage *reply; 1.1199 + 1.1200 + if (mConfirm) { 1.1201 + reply = dbus_message_new_method_return(msg); 1.1202 + } else { 1.1203 + reply = dbus_message_new_error(msg, "org.bluez.Error.Rejected", 1.1204 + "User rejected confirmation"); 1.1205 + } 1.1206 + 1.1207 + if (!reply) { 1.1208 + BT_WARNING("%s: Memory can't be allocated for the message.", __FUNCTION__); 1.1209 + dbus_message_unref(msg); 1.1210 + errorStr.AssignLiteral("Memory can't be allocated for the message."); 1.1211 + if (mRunnable) { 1.1212 + DispatchBluetoothReply(mRunnable, v, errorStr); 1.1213 + } 1.1214 + return; 1.1215 + } 1.1216 + 1.1217 + bool result = sDBusConnection->Send(reply); 1.1218 + if (!result) { 1.1219 + errorStr.AssignLiteral("Can't send message!"); 1.1220 + } 1.1221 + 1.1222 + dbus_message_unref(msg); 1.1223 + dbus_message_unref(reply); 1.1224 + sPairingReqTable->Remove(mDeviceAddress); 1.1225 + if (mRunnable) { 1.1226 + DispatchBluetoothReply(mRunnable, v, errorStr); 1.1227 + } 1.1228 + } 1.1229 + 1.1230 +private: 1.1231 + nsString mDeviceAddress; 1.1232 + bool mConfirm; 1.1233 + nsRefPtr<BluetoothReplyRunnable> mRunnable; 1.1234 +}; 1.1235 + 1.1236 +static DBusHandlerResult 1.1237 +AgentEventFilter(DBusConnection *conn, DBusMessage *msg, void *data) 1.1238 +{ 1.1239 + if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_METHOD_CALL) { 1.1240 + BT_WARNING("%s: agent handler not interested (not a method call).\n", 1.1241 + __FUNCTION__); 1.1242 + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 1.1243 + } 1.1244 + 1.1245 + DBusError err; 1.1246 + dbus_error_init(&err); 1.1247 + 1.1248 + BT_LOGD("%s: %s, %s", __FUNCTION__, 1.1249 + dbus_message_get_path(msg), 1.1250 + dbus_message_get_member(msg)); 1.1251 + 1.1252 + nsString signalPath = NS_ConvertUTF8toUTF16(dbus_message_get_path(msg)); 1.1253 + nsString signalName = NS_ConvertUTF8toUTF16(dbus_message_get_member(msg)); 1.1254 + nsString errorStr; 1.1255 + BluetoothValue v; 1.1256 + InfallibleTArray<BluetoothNamedValue> parameters; 1.1257 + bool isPairingReq = false; 1.1258 + BluetoothSignal signal(signalName, signalPath, v); 1.1259 + char *objectPath; 1.1260 + 1.1261 + // The following descriptions of each signal are retrieved from: 1.1262 + // 1.1263 + // http://maemo.org/api_refs/5.0/beta/bluez/agent.html 1.1264 + // 1.1265 + if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, "Cancel")) { 1.1266 + // This method gets called to indicate that the agent request failed before 1.1267 + // a reply was returned. 1.1268 + 1.1269 + // Return directly 1.1270 + DBusMessage *reply = dbus_message_new_method_return(msg); 1.1271 + 1.1272 + if (!reply) { 1.1273 + errorStr.AssignLiteral("Memory can't be allocated for the message."); 1.1274 + goto handle_error; 1.1275 + } 1.1276 + 1.1277 + dbus_connection_send(conn, reply, nullptr); 1.1278 + dbus_message_unref(reply); 1.1279 + v = parameters; 1.1280 + } else if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, "Authorize")) { 1.1281 + // This method gets called when the service daemon needs to authorize a 1.1282 + // connection/service request. 1.1283 + const char *uuid; 1.1284 + if (!dbus_message_get_args(msg, nullptr, 1.1285 + DBUS_TYPE_OBJECT_PATH, &objectPath, 1.1286 + DBUS_TYPE_STRING, &uuid, 1.1287 + DBUS_TYPE_INVALID)) { 1.1288 + errorStr.AssignLiteral("Invalid arguments for Authorize() method"); 1.1289 + goto handle_error; 1.1290 + } 1.1291 + 1.1292 + NS_ConvertUTF8toUTF16 uuidStr(uuid); 1.1293 + BluetoothServiceClass serviceClass = 1.1294 + BluetoothUuidHelper::GetBluetoothServiceClass(uuidStr); 1.1295 + if (serviceClass == BluetoothServiceClass::UNKNOWN) { 1.1296 + errorStr.AssignLiteral("Failed to get service class"); 1.1297 + goto handle_error; 1.1298 + } 1.1299 + 1.1300 + DBusMessage* reply = nullptr; 1.1301 + uint32_t i; 1.1302 + for (i = 0; i < MOZ_ARRAY_LENGTH(sAuthorizedServiceClass); i++) { 1.1303 + if (serviceClass == sAuthorizedServiceClass[i]) { 1.1304 + reply = dbus_message_new_method_return(msg); 1.1305 + break; 1.1306 + } 1.1307 + } 1.1308 + 1.1309 + // The uuid isn't authorized 1.1310 + if (i == MOZ_ARRAY_LENGTH(sAuthorizedServiceClass)) { 1.1311 + BT_WARNING("Uuid is not authorized."); 1.1312 + reply = dbus_message_new_error(msg, "org.bluez.Error.Rejected", 1.1313 + "The uuid is not authorized"); 1.1314 + } 1.1315 + 1.1316 + if (!reply) { 1.1317 + errorStr.AssignLiteral("Memory can't be allocated for the message."); 1.1318 + goto handle_error; 1.1319 + } 1.1320 + 1.1321 + dbus_connection_send(conn, reply, nullptr); 1.1322 + dbus_message_unref(reply); 1.1323 + return DBUS_HANDLER_RESULT_HANDLED; 1.1324 + } else if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, 1.1325 + "RequestConfirmation")) { 1.1326 + // This method gets called when the service daemon needs to confirm a 1.1327 + // passkey for an authentication. 1.1328 + uint32_t passkey; 1.1329 + if (!dbus_message_get_args(msg, nullptr, 1.1330 + DBUS_TYPE_OBJECT_PATH, &objectPath, 1.1331 + DBUS_TYPE_UINT32, &passkey, 1.1332 + DBUS_TYPE_INVALID)) { 1.1333 + errorStr.AssignLiteral("Invalid arguments: RequestConfirmation()"); 1.1334 + goto handle_error; 1.1335 + } 1.1336 + 1.1337 + parameters.AppendElement( 1.1338 + BluetoothNamedValue(NS_LITERAL_STRING("path"), 1.1339 + NS_ConvertUTF8toUTF16(objectPath))); 1.1340 + parameters.AppendElement( 1.1341 + BluetoothNamedValue(NS_LITERAL_STRING("method"), 1.1342 + NS_LITERAL_STRING("confirmation"))); 1.1343 + parameters.AppendElement( 1.1344 + BluetoothNamedValue(NS_LITERAL_STRING("passkey"), passkey)); 1.1345 + 1.1346 + v = parameters; 1.1347 + isPairingReq = true; 1.1348 + } else if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, 1.1349 + "RequestPinCode")) { 1.1350 + // This method gets called when the service daemon needs to get the passkey 1.1351 + // for an authentication. The return value should be a string of 1-16 1.1352 + // characters length. The string can be alphanumeric. 1.1353 + if (!dbus_message_get_args(msg, nullptr, 1.1354 + DBUS_TYPE_OBJECT_PATH, &objectPath, 1.1355 + DBUS_TYPE_INVALID)) { 1.1356 + errorStr.AssignLiteral("Invalid arguments for RequestPinCode() method"); 1.1357 + goto handle_error; 1.1358 + } 1.1359 + 1.1360 + parameters.AppendElement( 1.1361 + BluetoothNamedValue(NS_LITERAL_STRING("path"), 1.1362 + NS_ConvertUTF8toUTF16(objectPath))); 1.1363 + parameters.AppendElement( 1.1364 + BluetoothNamedValue(NS_LITERAL_STRING("method"), 1.1365 + NS_LITERAL_STRING("pincode"))); 1.1366 + 1.1367 + v = parameters; 1.1368 + isPairingReq = true; 1.1369 + } else if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, 1.1370 + "RequestPasskey")) { 1.1371 + // This method gets called when the service daemon needs to get the passkey 1.1372 + // for an authentication. The return value should be a numeric value 1.1373 + // between 0-999999. 1.1374 + if (!dbus_message_get_args(msg, nullptr, 1.1375 + DBUS_TYPE_OBJECT_PATH, &objectPath, 1.1376 + DBUS_TYPE_INVALID)) { 1.1377 + errorStr.AssignLiteral("Invalid arguments for RequestPasskey() method"); 1.1378 + goto handle_error; 1.1379 + } 1.1380 + 1.1381 + parameters.AppendElement(BluetoothNamedValue( 1.1382 + NS_LITERAL_STRING("path"), 1.1383 + NS_ConvertUTF8toUTF16(objectPath))); 1.1384 + parameters.AppendElement(BluetoothNamedValue( 1.1385 + NS_LITERAL_STRING("method"), 1.1386 + NS_LITERAL_STRING("passkey"))); 1.1387 + 1.1388 + v = parameters; 1.1389 + isPairingReq = true; 1.1390 + } else if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, "Release")) { 1.1391 + // This method gets called when the service daemon unregisters the agent. 1.1392 + // An agent can use it to do cleanup tasks. There is no need to unregister 1.1393 + // the agent, because when this method gets called it has already been 1.1394 + // unregistered. 1.1395 + DBusMessage *reply = dbus_message_new_method_return(msg); 1.1396 + 1.1397 + if (!reply) { 1.1398 + errorStr.AssignLiteral("Memory can't be allocated for the message."); 1.1399 + goto handle_error; 1.1400 + } 1.1401 + 1.1402 + dbus_connection_send(conn, reply, nullptr); 1.1403 + dbus_message_unref(reply); 1.1404 + 1.1405 + // Do not send a notification to upper layer, too annoying. 1.1406 + return DBUS_HANDLER_RESULT_HANDLED; 1.1407 + } else if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, "RequestPairingConsent")) { 1.1408 + // Directly SetPairingconfirmation for RequestPairingConsent here 1.1409 + if (!dbus_message_get_args(msg, nullptr, 1.1410 + DBUS_TYPE_OBJECT_PATH, &objectPath, 1.1411 + DBUS_TYPE_INVALID)) { 1.1412 + errorStr.AssignLiteral("Invalid arguments: RequestPairingConsent()"); 1.1413 + goto handle_error; 1.1414 + } 1.1415 + 1.1416 + nsString address = GetAddressFromObjectPath(NS_ConvertUTF8toUTF16(objectPath)); 1.1417 + sPairingReqTable->Put(address, msg); 1.1418 + Task* task = new SetPairingConfirmationTask(address, true, nullptr); 1.1419 + DispatchToDBusThread(task); 1.1420 + // Increase dbus message reference counts, it will be decreased in 1.1421 + // SetPairingConfirmationTask 1.1422 + dbus_message_ref(msg); 1.1423 + // Do not send a notification to upper layer 1.1424 + return DBUS_HANDLER_RESULT_HANDLED; 1.1425 + } else { 1.1426 +#ifdef DEBUG 1.1427 + BT_WARNING("agent handler %s: Unhandled event. Ignore.", __FUNCTION__); 1.1428 +#endif 1.1429 + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 1.1430 + } 1.1431 + 1.1432 + if (!errorStr.IsEmpty()) { 1.1433 + BT_WARNING(NS_ConvertUTF16toUTF8(errorStr).get()); 1.1434 + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 1.1435 + } 1.1436 + 1.1437 + // Update value after parsing DBus message 1.1438 + signal.value() = v; 1.1439 + 1.1440 + if (isPairingReq) { 1.1441 + sPairingReqTable->Put( 1.1442 + GetAddressFromObjectPath(NS_ConvertUTF8toUTF16(objectPath)), msg); 1.1443 + 1.1444 + // Increase ref count here because we need this message later. 1.1445 + // It'll be unrefed when set*Internal() is called. 1.1446 + dbus_message_ref(msg); 1.1447 + 1.1448 + AppendDeviceName(signal); 1.1449 + } else { 1.1450 + NS_DispatchToMainThread(new DistributeBluetoothSignalTask(signal)); 1.1451 + } 1.1452 + 1.1453 + return DBUS_HANDLER_RESULT_HANDLED; 1.1454 + 1.1455 +handle_error: 1.1456 + BT_WARNING(NS_ConvertUTF16toUTF8(errorStr).get()); 1.1457 + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 1.1458 +} 1.1459 + 1.1460 +class RegisterAgentReplyHandler : public DBusReplyHandler 1.1461 +{ 1.1462 +public: 1.1463 + RegisterAgentReplyHandler(const DBusObjectPathVTable* aAgentVTable) 1.1464 + : mAgentVTable(aAgentVTable) 1.1465 + { 1.1466 + MOZ_ASSERT(aAgentVTable); 1.1467 + } 1.1468 + 1.1469 + void Handle(DBusMessage* aReply) 1.1470 + { 1.1471 + MOZ_ASSERT(!NS_IsMainThread()); // I/O thread 1.1472 + MOZ_ASSERT(sDBusConnection); 1.1473 + 1.1474 + if (!aReply || (dbus_message_get_type(aReply) == DBUS_MESSAGE_TYPE_ERROR)) { 1.1475 + return; 1.1476 + } 1.1477 + 1.1478 + // There is no "RegisterAgent" function defined in device interface. 1.1479 + // When we call "CreatePairedDevice", it will do device agent registration 1.1480 + // for us. (See maemo.org/api_refs/5.0/beta/bluez/adapter.html) 1.1481 + if (!dbus_connection_register_object_path(sDBusConnection->GetConnection(), 1.1482 + KEY_REMOTE_AGENT, 1.1483 + mAgentVTable, 1.1484 + nullptr)) { 1.1485 + BT_WARNING("%s: Can't register object path %s for remote device agent!", 1.1486 + __FUNCTION__, KEY_REMOTE_AGENT); 1.1487 + return; 1.1488 + } 1.1489 + 1.1490 + NS_DispatchToMainThread(new PrepareProfileManagersRunnable()); 1.1491 + } 1.1492 + 1.1493 +private: 1.1494 + const DBusObjectPathVTable* mAgentVTable; 1.1495 +}; 1.1496 + 1.1497 +class AddReservedServiceRecordsReplyHandler : public DBusReplyHandler 1.1498 +{ 1.1499 +public: 1.1500 + void Handle(DBusMessage* aReply) 1.1501 + { 1.1502 + static const DBusObjectPathVTable sAgentVTable = { 1.1503 + nullptr, AgentEventFilter, nullptr, nullptr, nullptr, nullptr 1.1504 + }; 1.1505 + 1.1506 + MOZ_ASSERT(!NS_IsMainThread()); // I/O thread 1.1507 + 1.1508 + if (!aReply || (dbus_message_get_type(aReply) == DBUS_MESSAGE_TYPE_ERROR)) { 1.1509 + return; 1.1510 + } 1.1511 + 1.1512 + // TODO/qdot: This needs to be held for the life of the bluetooth connection 1.1513 + // so we could clean it up. For right now though, we can throw it away. 1.1514 + nsTArray<uint32_t> handles; 1.1515 + 1.1516 + ExtractHandles(aReply, handles); 1.1517 + 1.1518 + if(!RegisterAgent(&sAgentVTable)) { 1.1519 + BT_WARNING("Failed to register agent"); 1.1520 + } 1.1521 + } 1.1522 + 1.1523 +private: 1.1524 + void ExtractHandles(DBusMessage *aMessage, nsTArray<uint32_t>& aOutHandles) 1.1525 + { 1.1526 + DBusError error; 1.1527 + int length; 1.1528 + uint32_t* handles = nullptr; 1.1529 + 1.1530 + dbus_error_init(&error); 1.1531 + 1.1532 + bool success = dbus_message_get_args(aMessage, &error, 1.1533 + DBUS_TYPE_ARRAY, 1.1534 + DBUS_TYPE_UINT32, 1.1535 + &handles, &length, 1.1536 + DBUS_TYPE_INVALID); 1.1537 + if (success != TRUE) { 1.1538 + LOG_AND_FREE_DBUS_ERROR(&error); 1.1539 + return; 1.1540 + } 1.1541 + 1.1542 + if (!handles) { 1.1543 + BT_WARNING("Null array in extract_handles"); 1.1544 + return; 1.1545 + } 1.1546 + 1.1547 + for (int i = 0; i < length; ++i) { 1.1548 + aOutHandles.AppendElement(handles[i]); 1.1549 + } 1.1550 + } 1.1551 + 1.1552 + bool RegisterAgent(const DBusObjectPathVTable* aAgentVTable) 1.1553 + { 1.1554 + const char* agentPath = KEY_LOCAL_AGENT; 1.1555 + const char* capabilities = B2G_AGENT_CAPABILITIES; 1.1556 + 1.1557 + MOZ_ASSERT(sDBusConnection); 1.1558 + 1.1559 + // Local agent means agent for Adapter, not agent for Device. Some signals 1.1560 + // will be passed to local agent, some will be passed to device agent. 1.1561 + // For example, if a remote device would like to pair with us, then the 1.1562 + // signal will be passed to local agent. If we start pairing process with 1.1563 + // calling CreatePairedDevice, we'll get signal which should be passed to 1.1564 + // device agent. 1.1565 + if (!dbus_connection_register_object_path(sDBusConnection->GetConnection(), 1.1566 + KEY_LOCAL_AGENT, 1.1567 + aAgentVTable, 1.1568 + nullptr)) { 1.1569 + BT_WARNING("%s: Can't register object path %s for agent!", 1.1570 + __FUNCTION__, KEY_LOCAL_AGENT); 1.1571 + return false; 1.1572 + } 1.1573 + 1.1574 + nsRefPtr<RegisterAgentReplyHandler> handler = 1.1575 + new RegisterAgentReplyHandler(aAgentVTable); 1.1576 + MOZ_ASSERT(!sAdapterPath.IsEmpty()); 1.1577 + 1.1578 + bool success = sDBusConnection->SendWithReply( 1.1579 + RegisterAgentReplyHandler::Callback, handler.get(), -1, 1.1580 + BLUEZ_DBUS_BASE_IFC, 1.1581 + NS_ConvertUTF16toUTF8(sAdapterPath).get(), 1.1582 + DBUS_ADAPTER_IFACE, "RegisterAgent", 1.1583 + DBUS_TYPE_OBJECT_PATH, &agentPath, 1.1584 + DBUS_TYPE_STRING, &capabilities, 1.1585 + DBUS_TYPE_INVALID); 1.1586 + 1.1587 + NS_ENSURE_TRUE(success, false); 1.1588 + 1.1589 + unused << handler.forget(); // picked up by callback handler 1.1590 + 1.1591 + return true; 1.1592 + } 1.1593 +}; 1.1594 + 1.1595 +class AddReservedServiceRecordsTask : public Task 1.1596 +{ 1.1597 +public: 1.1598 + AddReservedServiceRecordsTask() 1.1599 + { } 1.1600 + 1.1601 + void Run() 1.1602 + { 1.1603 + static const dbus_uint32_t sServices[] = { 1.1604 + BluetoothServiceClass::HANDSFREE_AG, 1.1605 + BluetoothServiceClass::HEADSET_AG, 1.1606 + BluetoothServiceClass::OBJECT_PUSH 1.1607 + }; 1.1608 + 1.1609 + MOZ_ASSERT(!NS_IsMainThread()); // I/O thread 1.1610 + MOZ_ASSERT(sDBusConnection); 1.1611 + MOZ_ASSERT(!sAdapterPath.IsEmpty()); 1.1612 + 1.1613 + nsRefPtr<DBusReplyHandler> handler = 1.1614 + new AddReservedServiceRecordsReplyHandler(); 1.1615 + 1.1616 + const dbus_uint32_t* services = sServices; 1.1617 + 1.1618 + bool success = sDBusConnection->SendWithReply( 1.1619 + DBusReplyHandler::Callback, handler.get(), -1, 1.1620 + BLUEZ_DBUS_BASE_IFC, 1.1621 + NS_ConvertUTF16toUTF8(sAdapterPath).get(), 1.1622 + DBUS_ADAPTER_IFACE, "AddReservedServiceRecords", 1.1623 + DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, 1.1624 + &services, ArrayLength(sServices), DBUS_TYPE_INVALID); 1.1625 + 1.1626 + NS_ENSURE_TRUE_VOID(success); 1.1627 + 1.1628 + unused << handler.forget(); /* picked up by callback handler */ 1.1629 + } 1.1630 +}; 1.1631 + 1.1632 +class PrepareAdapterRunnable : public nsRunnable 1.1633 +{ 1.1634 +public: 1.1635 + PrepareAdapterRunnable() 1.1636 + { } 1.1637 + 1.1638 + NS_IMETHOD Run() 1.1639 + { 1.1640 + MOZ_ASSERT(NS_IsMainThread()); 1.1641 + 1.1642 + Task* task = new AddReservedServiceRecordsTask(); 1.1643 + DispatchToDBusThread(task); 1.1644 + 1.1645 + return NS_OK; 1.1646 + } 1.1647 +}; 1.1648 + 1.1649 +class RequestPlayStatusTask : public nsRunnable 1.1650 +{ 1.1651 +public: 1.1652 + RequestPlayStatusTask() 1.1653 + { 1.1654 + MOZ_ASSERT(!NS_IsMainThread()); // I/O thread 1.1655 + } 1.1656 + 1.1657 + nsresult Run() 1.1658 + { 1.1659 + MOZ_ASSERT(NS_IsMainThread()); 1.1660 + 1.1661 + BluetoothSignal signal(NS_LITERAL_STRING(REQUEST_MEDIA_PLAYSTATUS_ID), 1.1662 + NS_LITERAL_STRING(KEY_ADAPTER), 1.1663 + InfallibleTArray<BluetoothNamedValue>()); 1.1664 + 1.1665 + BluetoothService* bs = BluetoothService::Get(); 1.1666 + NS_ENSURE_TRUE(bs, NS_ERROR_FAILURE); 1.1667 + bs->DistributeSignal(signal); 1.1668 + 1.1669 + return NS_OK; 1.1670 + } 1.1671 +}; 1.1672 + 1.1673 +// Called by dbus during WaitForAndDispatchEventNative() 1.1674 +// This function is called on the IOThread 1.1675 +static DBusHandlerResult 1.1676 +EventFilter(DBusConnection* aConn, DBusMessage* aMsg, void* aData) 1.1677 +{ 1.1678 + // I/O thread 1.1679 + MOZ_ASSERT(!NS_IsMainThread(), "Shouldn't be called from Main Thread!"); 1.1680 + 1.1681 + if (dbus_message_get_type(aMsg) != DBUS_MESSAGE_TYPE_SIGNAL) { 1.1682 + BT_WARNING("%s: event handler not interested in %s (not a signal).\n", 1.1683 + __FUNCTION__, dbus_message_get_member(aMsg)); 1.1684 + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 1.1685 + } 1.1686 + 1.1687 + if (dbus_message_get_path(aMsg) == nullptr) { 1.1688 + BT_WARNING("DBusMessage %s has no bluetooth destination, ignoring\n", 1.1689 + dbus_message_get_member(aMsg)); 1.1690 + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 1.1691 + } 1.1692 + 1.1693 + DBusError err; 1.1694 + dbus_error_init(&err); 1.1695 + 1.1696 + nsAutoString signalPath; 1.1697 + nsAutoString signalName; 1.1698 + nsAutoString signalInterface; 1.1699 + 1.1700 + BT_LOGD("%s: %s, %s, %s", __FUNCTION__, 1.1701 + dbus_message_get_interface(aMsg), 1.1702 + dbus_message_get_path(aMsg), 1.1703 + dbus_message_get_member(aMsg)); 1.1704 + 1.1705 + signalInterface = NS_ConvertUTF8toUTF16(dbus_message_get_interface(aMsg)); 1.1706 + signalPath = NS_ConvertUTF8toUTF16(dbus_message_get_path(aMsg)); 1.1707 + signalName = NS_ConvertUTF8toUTF16(dbus_message_get_member(aMsg)); 1.1708 + nsString errorStr; 1.1709 + BluetoothValue v; 1.1710 + 1.1711 + // Since the signalPath extracted from dbus message is a object path, 1.1712 + // we'd like to re-assign them to corresponding key entry in 1.1713 + // BluetoothSignalObserverTable 1.1714 + if (signalInterface.EqualsLiteral(DBUS_MANAGER_IFACE)) { 1.1715 + signalPath.AssignLiteral(KEY_MANAGER); 1.1716 + } else if (signalInterface.EqualsLiteral(DBUS_ADAPTER_IFACE)) { 1.1717 + signalPath.AssignLiteral(KEY_ADAPTER); 1.1718 + } else if (signalInterface.EqualsLiteral(DBUS_DEVICE_IFACE)){ 1.1719 + signalPath = GetAddressFromObjectPath(signalPath); 1.1720 + } 1.1721 + 1.1722 + if (dbus_message_is_signal(aMsg, DBUS_ADAPTER_IFACE, "DeviceFound")) { 1.1723 + DBusMessageIter iter; 1.1724 + 1.1725 + if (!dbus_message_iter_init(aMsg, &iter)) { 1.1726 + BT_WARNING("Can't create iterator!"); 1.1727 + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 1.1728 + } 1.1729 + 1.1730 + const char* addr; 1.1731 + dbus_message_iter_get_basic(&iter, &addr); 1.1732 + 1.1733 + if (!dbus_message_iter_next(&iter)) { 1.1734 + errorStr.AssignLiteral("Unexpected message struct in msg DeviceFound"); 1.1735 + } else { 1.1736 + ParseProperties(&iter, 1.1737 + v, 1.1738 + errorStr, 1.1739 + sDeviceProperties, 1.1740 + ArrayLength(sDeviceProperties)); 1.1741 + 1.1742 + InfallibleTArray<BluetoothNamedValue>& properties = 1.1743 + v.get_ArrayOfBluetoothNamedValue(); 1.1744 + 1.1745 + // The DBus DeviceFound message actually passes back a key value object 1.1746 + // with the address as the key and the rest of the device properties as 1.1747 + // a dict value. After we parse out the properties, we need to go back 1.1748 + // and add the address to the ipdl dict we've created to make sure we 1.1749 + // have all of the information to correctly build the device. 1.1750 + nsAutoString address = NS_ConvertUTF8toUTF16(addr); 1.1751 + properties.AppendElement( 1.1752 + BluetoothNamedValue(NS_LITERAL_STRING("Address"), address)); 1.1753 + properties.AppendElement( 1.1754 + BluetoothNamedValue(NS_LITERAL_STRING("Path"), 1.1755 + GetObjectPathFromAddress(signalPath, address))); 1.1756 + 1.1757 + if (!ContainsIcon(properties)) { 1.1758 + for (uint32_t i = 0; i < properties.Length(); i++) { 1.1759 + // It is possible that property Icon missed due to CoD of major 1.1760 + // class is TOY but service class is "Audio", we need to assign 1.1761 + // Icon as audio-card. This is for PTS test TC_AG_COD_BV_02_I. 1.1762 + // As HFP specification defined that 1.1763 + // service class is "Audio" can be considered as HFP AG. 1.1764 + if (properties[i].name().EqualsLiteral("Class")) { 1.1765 + if (HasAudioService(properties[i].value().get_uint32_t())) { 1.1766 + v.get_ArrayOfBluetoothNamedValue().AppendElement( 1.1767 + BluetoothNamedValue(NS_LITERAL_STRING("Icon"), 1.1768 + NS_LITERAL_STRING("audio-card"))); 1.1769 + } 1.1770 + break; 1.1771 + } 1.1772 + } 1.1773 + } 1.1774 + } 1.1775 + } else if (dbus_message_is_signal(aMsg, DBUS_ADAPTER_IFACE, 1.1776 + "DeviceDisappeared")) { 1.1777 + const char* str; 1.1778 + if (!dbus_message_get_args(aMsg, &err, 1.1779 + DBUS_TYPE_STRING, &str, 1.1780 + DBUS_TYPE_INVALID)) { 1.1781 + LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, aMsg); 1.1782 + errorStr.AssignLiteral("Cannot parse device address!"); 1.1783 + } else { 1.1784 + v = NS_ConvertUTF8toUTF16(str); 1.1785 + } 1.1786 + } else if (dbus_message_is_signal(aMsg, DBUS_ADAPTER_IFACE, 1.1787 + "DeviceCreated")) { 1.1788 + const char* str; 1.1789 + if (!dbus_message_get_args(aMsg, &err, 1.1790 + DBUS_TYPE_OBJECT_PATH, &str, 1.1791 + DBUS_TYPE_INVALID)) { 1.1792 + LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, aMsg); 1.1793 + errorStr.AssignLiteral("Cannot parse device path!"); 1.1794 + } else { 1.1795 + v = NS_ConvertUTF8toUTF16(str); 1.1796 + } 1.1797 + } else if (dbus_message_is_signal(aMsg, DBUS_ADAPTER_IFACE, 1.1798 + "DeviceRemoved")) { 1.1799 + const char* str; 1.1800 + if (!dbus_message_get_args(aMsg, &err, 1.1801 + DBUS_TYPE_OBJECT_PATH, &str, 1.1802 + DBUS_TYPE_INVALID)) { 1.1803 + LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, aMsg); 1.1804 + errorStr.AssignLiteral("Cannot parse device path!"); 1.1805 + } else { 1.1806 + v = NS_ConvertUTF8toUTF16(str); 1.1807 + } 1.1808 + } else if (dbus_message_is_signal(aMsg, DBUS_ADAPTER_IFACE, 1.1809 + "PropertyChanged")) { 1.1810 + ParsePropertyChange(aMsg, 1.1811 + v, 1.1812 + errorStr, 1.1813 + sAdapterProperties, 1.1814 + ArrayLength(sAdapterProperties)); 1.1815 + } else if (dbus_message_is_signal(aMsg, DBUS_DEVICE_IFACE, 1.1816 + "PropertyChanged")) { 1.1817 + ParsePropertyChange(aMsg, 1.1818 + v, 1.1819 + errorStr, 1.1820 + sDeviceProperties, 1.1821 + ArrayLength(sDeviceProperties)); 1.1822 + 1.1823 + if (v.type() == BluetoothValue::T__None) { 1.1824 + BT_WARNING("PropertyChanged event couldn't be parsed."); 1.1825 + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 1.1826 + } 1.1827 + 1.1828 + BluetoothNamedValue& property = v.get_ArrayOfBluetoothNamedValue()[0]; 1.1829 + if (property.name().EqualsLiteral("Paired")) { 1.1830 + // Original approach: Broadcast system message of 1.1831 + // "bluetooth-pairedstatuschanged" from BluetoothService. 1.1832 + BluetoothValue newValue(v); 1.1833 + ToLowerCase(newValue.get_ArrayOfBluetoothNamedValue()[0].name()); 1.1834 + BluetoothSignal signal(NS_LITERAL_STRING(PAIRED_STATUS_CHANGED_ID), 1.1835 + NS_LITERAL_STRING(KEY_LOCAL_AGENT), 1.1836 + newValue); 1.1837 + NS_DispatchToMainThread(new DistributeBluetoothSignalTask(signal)); 1.1838 + 1.1839 + // New approach: Dispatch event from BluetoothAdapter 1.1840 + bool status = property.value(); 1.1841 + InfallibleTArray<BluetoothNamedValue> parameters; 1.1842 + parameters.AppendElement( 1.1843 + BluetoothNamedValue(NS_LITERAL_STRING("address"), signalPath)); 1.1844 + parameters.AppendElement( 1.1845 + BluetoothNamedValue(NS_LITERAL_STRING("status"), status)); 1.1846 + signal.path() = NS_LITERAL_STRING(KEY_ADAPTER); 1.1847 + signal.value() = parameters; 1.1848 + NS_DispatchToMainThread(new DistributeBluetoothSignalTask(signal)); 1.1849 + } else if (property.name().EqualsLiteral("Connected")) { 1.1850 + MonitorAutoLock lock(*sStopBluetoothMonitor); 1.1851 + 1.1852 + if (property.value().get_bool()) { 1.1853 + ++sConnectedDeviceCount; 1.1854 + } else { 1.1855 + MOZ_ASSERT(sConnectedDeviceCount > 0); 1.1856 + if (--sConnectedDeviceCount == 0) { 1.1857 + lock.Notify(); 1.1858 + } 1.1859 + } 1.1860 + } 1.1861 + } else if (dbus_message_is_signal(aMsg, DBUS_MANAGER_IFACE, "AdapterAdded")) { 1.1862 + const char* str; 1.1863 + if (!dbus_message_get_args(aMsg, &err, 1.1864 + DBUS_TYPE_OBJECT_PATH, &str, 1.1865 + DBUS_TYPE_INVALID)) { 1.1866 + LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, aMsg); 1.1867 + errorStr.AssignLiteral("Cannot parse manager path!"); 1.1868 + } else { 1.1869 + v = NS_ConvertUTF8toUTF16(str); 1.1870 + sAdapterPath = v.get_nsString(); 1.1871 + NS_DispatchToMainThread(new TryFiringAdapterAddedRunnable(true)); 1.1872 + NS_DispatchToMainThread(new PrepareAdapterRunnable()); 1.1873 + 1.1874 + /** 1.1875 + * The adapter name isn't ready for the time being. Wait for the upcoming 1.1876 + * signal PropertyChanged of adapter name, and then propagate signal 1.1877 + * AdapterAdded to BluetoothManager. 1.1878 + */ 1.1879 + return DBUS_HANDLER_RESULT_HANDLED; 1.1880 + } 1.1881 + } else if (dbus_message_is_signal(aMsg, DBUS_MANAGER_IFACE, 1.1882 + "PropertyChanged")) { 1.1883 + ParsePropertyChange(aMsg, 1.1884 + v, 1.1885 + errorStr, 1.1886 + sManagerProperties, 1.1887 + ArrayLength(sManagerProperties)); 1.1888 + } else if (dbus_message_is_signal(aMsg, DBUS_SINK_IFACE, 1.1889 + "PropertyChanged")) { 1.1890 + ParsePropertyChange(aMsg, 1.1891 + v, 1.1892 + errorStr, 1.1893 + sSinkProperties, 1.1894 + ArrayLength(sSinkProperties)); 1.1895 + } else if (dbus_message_is_signal(aMsg, DBUS_CTL_IFACE, "GetPlayStatus")) { 1.1896 + NS_DispatchToMainThread(new RequestPlayStatusTask()); 1.1897 + return DBUS_HANDLER_RESULT_HANDLED; 1.1898 + } else if (dbus_message_is_signal(aMsg, DBUS_CTL_IFACE, "PropertyChanged")) { 1.1899 + ParsePropertyChange(aMsg, 1.1900 + v, 1.1901 + errorStr, 1.1902 + sControlProperties, 1.1903 + ArrayLength(sControlProperties)); 1.1904 + } else if (dbus_message_is_signal(aMsg, DBUS_INPUT_IFACE, 1.1905 + "PropertyChanged")) { 1.1906 + ParsePropertyChange(aMsg, 1.1907 + v, 1.1908 + errorStr, 1.1909 + sInputProperties, 1.1910 + ArrayLength(sInputProperties)); 1.1911 + } else { 1.1912 + errorStr = NS_ConvertUTF8toUTF16(dbus_message_get_member(aMsg)); 1.1913 + errorStr.AppendLiteral(" Signal not handled!"); 1.1914 + } 1.1915 + 1.1916 + if (!errorStr.IsEmpty()) { 1.1917 + BT_WARNING(NS_ConvertUTF16toUTF8(errorStr).get()); 1.1918 + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 1.1919 + } 1.1920 + 1.1921 + BluetoothSignal signal(signalName, signalPath, v); 1.1922 + nsRefPtr<nsRunnable> task; 1.1923 + if (signalInterface.EqualsLiteral(DBUS_SINK_IFACE)) { 1.1924 + task = new SinkPropertyChangedHandler(signal); 1.1925 + } else if (signalInterface.EqualsLiteral(DBUS_CTL_IFACE)) { 1.1926 + task = new ControlPropertyChangedHandler(signal); 1.1927 + } else if (signalInterface.EqualsLiteral(DBUS_INPUT_IFACE)) { 1.1928 + task = new InputPropertyChangedHandler(signal); 1.1929 + } else { 1.1930 + task = new DistributeBluetoothSignalTask(signal); 1.1931 + } 1.1932 + 1.1933 + NS_DispatchToMainThread(task); 1.1934 + 1.1935 + return DBUS_HANDLER_RESULT_HANDLED; 1.1936 +} 1.1937 + 1.1938 +static void 1.1939 +OnDefaultAdapterReply(DBusMessage* aReply, void* aData) 1.1940 +{ 1.1941 + MOZ_ASSERT(!NS_IsMainThread()); // I/O thread 1.1942 + 1.1943 + if (!aReply || dbus_message_is_error(aReply, DBUS_ERROR_TIMEOUT)) { 1.1944 + return; 1.1945 + } 1.1946 + 1.1947 + DBusError err; 1.1948 + dbus_error_init(&err); 1.1949 + 1.1950 + BluetoothValue v; 1.1951 + nsAutoString errorString; 1.1952 + 1.1953 + UnpackObjectPathMessage(aReply, &err, v, errorString); 1.1954 + 1.1955 + if (!errorString.IsEmpty()) { 1.1956 + return; 1.1957 + } 1.1958 + 1.1959 + sAdapterPath = v.get_nsString(); 1.1960 + 1.1961 + nsRefPtr<PrepareAdapterRunnable> b = new PrepareAdapterRunnable(); 1.1962 + if (NS_FAILED(NS_DispatchToMainThread(b))) { 1.1963 + BT_WARNING("Failed to dispatch to main thread!"); 1.1964 + } 1.1965 +} 1.1966 + 1.1967 +bool 1.1968 +BluetoothDBusService::IsReady() 1.1969 +{ 1.1970 + if (!IsEnabled() || !sDBusConnection || IsToggling()) { 1.1971 + BT_WARNING("Bluetooth service is not ready yet!"); 1.1972 + return false; 1.1973 + } 1.1974 + return true; 1.1975 +} 1.1976 + 1.1977 +class StartDBusConnectionTask : public Task 1.1978 +{ 1.1979 +public: 1.1980 + StartDBusConnectionTask(RawDBusConnection* aConnection) 1.1981 + : mConnection(aConnection) 1.1982 + { 1.1983 + MOZ_ASSERT(mConnection); 1.1984 + } 1.1985 + 1.1986 + void Run() 1.1987 + { 1.1988 + MOZ_ASSERT(!NS_IsMainThread()); // I/O thread 1.1989 + 1.1990 + if (sDBusConnection) { 1.1991 + BT_WARNING("DBus connection has already been established."); 1.1992 + nsRefPtr<nsRunnable> runnable = new BluetoothService::ToggleBtAck(true); 1.1993 + if (NS_FAILED(NS_DispatchToMainThread(runnable))) { 1.1994 + BT_WARNING("Failed to dispatch to main thread!"); 1.1995 + } 1.1996 + return; 1.1997 + } 1.1998 + 1.1999 + // Add a filter for all incoming messages_base 1.2000 + if (!dbus_connection_add_filter(mConnection->GetConnection(), 1.2001 + EventFilter, nullptr, nullptr)) { 1.2002 + BT_WARNING("Cannot create DBus Event Filter for DBus Thread!"); 1.2003 + nsRefPtr<nsRunnable> runnable = new BluetoothService::ToggleBtAck(false); 1.2004 + if (NS_FAILED(NS_DispatchToMainThread(runnable))) { 1.2005 + BT_WARNING("Failed to dispatch to main thread!"); 1.2006 + } 1.2007 + return; 1.2008 + } 1.2009 + 1.2010 + mConnection->Watch(); 1.2011 + 1.2012 + if (!sPairingReqTable) { 1.2013 + sPairingReqTable = new nsDataHashtable<nsStringHashKey, DBusMessage* >; 1.2014 + } 1.2015 + 1.2016 + sDBusConnection = mConnection.forget(); 1.2017 + 1.2018 + nsRefPtr<nsRunnable> runnable = 1.2019 + new BluetoothService::ToggleBtAck(true); 1.2020 + if (NS_FAILED(NS_DispatchToMainThread(runnable))) { 1.2021 + BT_WARNING("Failed to dispatch to main thread!"); 1.2022 + return; 1.2023 + } 1.2024 + 1.2025 + /* Normally we'll receive the signal 'AdapterAdded' with the adapter object 1.2026 + * path from the DBus daemon during start up. So, there's no need to query 1.2027 + * the object path of default adapter here. However, if we restart from a 1.2028 + * crash, the default adapter might already be available, so we ask the daemon 1.2029 + * explicitly here. 1.2030 + */ 1.2031 + if (sAdapterPath.IsEmpty()) { 1.2032 + bool success = sDBusConnection->SendWithReply(OnDefaultAdapterReply, nullptr, 1.2033 + 1000, BLUEZ_DBUS_BASE_IFC, "/", 1.2034 + DBUS_MANAGER_IFACE, 1.2035 + "DefaultAdapter", 1.2036 + DBUS_TYPE_INVALID); 1.2037 + if (!success) { 1.2038 + BT_WARNING("Failed to query default adapter!"); 1.2039 + } 1.2040 + } 1.2041 + } 1.2042 + 1.2043 +private: 1.2044 + nsAutoPtr<RawDBusConnection> mConnection; 1.2045 +}; 1.2046 + 1.2047 +class StartBluetoothRunnable MOZ_FINAL : public nsRunnable 1.2048 +{ 1.2049 +public: 1.2050 + NS_IMETHOD Run() 1.2051 + { 1.2052 + // This could block. It should never be run on the main thread. 1.2053 + MOZ_ASSERT(!NS_IsMainThread()); // BT thread 1.2054 + 1.2055 +#ifdef MOZ_WIDGET_GONK 1.2056 + if (!sBluedroid.Enable()) { 1.2057 + BT_WARNING("Bluetooth not available."); 1.2058 + nsRefPtr<nsRunnable> runnable = new BluetoothService::ToggleBtAck(false); 1.2059 + if (NS_FAILED(NS_DispatchToMainThread(runnable))) { 1.2060 + BT_WARNING("Failed to dispatch to main thread!"); 1.2061 + } 1.2062 + return NS_ERROR_FAILURE; 1.2063 + } 1.2064 +#endif 1.2065 + 1.2066 + RawDBusConnection* connection = new RawDBusConnection(); 1.2067 + nsresult rv = connection->EstablishDBusConnection(); 1.2068 + if (NS_FAILED(rv)) { 1.2069 + BT_WARNING("Failed to establish connection to BlueZ daemon"); 1.2070 + nsRefPtr<nsRunnable> runnable = new BluetoothService::ToggleBtAck(false); 1.2071 + if (NS_FAILED(NS_DispatchToMainThread(runnable))) { 1.2072 + BT_WARNING("Failed to dispatch to main thread!"); 1.2073 + } 1.2074 + return NS_ERROR_FAILURE; 1.2075 + } 1.2076 + 1.2077 + DBusError err; 1.2078 + dbus_error_init(&err); 1.2079 + 1.2080 + // Set which messages will be processed by this dbus connection. 1.2081 + // Since we are maintaining a single thread for all the DBus bluez 1.2082 + // signals we want, register all of them in this thread at startup. 1.2083 + // The event handler will sort the destinations out as needed. The 1.2084 + // call to dbus_bus_add_match has to run on the BT thread because 1.2085 + // it can block. 1.2086 + for (uint32_t i = 0; i < ArrayLength(sBluetoothDBusSignals); ++i) { 1.2087 + dbus_bus_add_match(connection->GetConnection(), 1.2088 + sBluetoothDBusSignals[i], 1.2089 + &err); 1.2090 + if (dbus_error_is_set(&err)) { 1.2091 + LOG_AND_FREE_DBUS_ERROR(&err); 1.2092 + } 1.2093 + } 1.2094 + 1.2095 + Task* task = new StartDBusConnectionTask(connection); 1.2096 + DispatchToDBusThread(task); 1.2097 + 1.2098 + return NS_OK; 1.2099 + } 1.2100 +}; 1.2101 + 1.2102 +nsresult 1.2103 +BluetoothDBusService::StartInternal() 1.2104 +{ 1.2105 + nsRefPtr<nsRunnable> runnable = new StartBluetoothRunnable(); 1.2106 + nsresult rv = DispatchToBtThread(runnable); 1.2107 + if (NS_FAILED(rv)) { 1.2108 + BT_WARNING("Failed to dispatch to BT thread!"); 1.2109 + } 1.2110 + return rv; 1.2111 +} 1.2112 + 1.2113 +class DisableBluetoothRunnable MOZ_FINAL : public nsRunnable 1.2114 +{ 1.2115 +public: 1.2116 + NS_IMETHOD Run() 1.2117 + { 1.2118 + if (NS_IsMainThread()) { 1.2119 + // Clear |sControllerArray| here while we're on the main thread 1.2120 + sControllerArray.Clear(); 1.2121 + // Forward this runnable to BT thread 1.2122 + return DispatchToBtThread(this); 1.2123 + } 1.2124 + 1.2125 +#ifdef MOZ_WIDGET_GONK 1.2126 + MOZ_ASSERT(sBluedroid.IsEnabled()); 1.2127 + // Disable() return true on success, so we need to invert it 1.2128 + bool isEnabled = !sBluedroid.Disable(); 1.2129 +#else 1.2130 + bool isEnabled = false; 1.2131 +#endif 1.2132 + 1.2133 + nsRefPtr<nsRunnable> runnable = 1.2134 + new BluetoothService::ToggleBtAck(isEnabled); 1.2135 + nsresult rv = NS_DispatchToMainThread(runnable); 1.2136 + if (NS_FAILED(rv)) { 1.2137 + BT_WARNING("Failed to dispatch to main thread!"); 1.2138 + } 1.2139 + return rv; 1.2140 + } 1.2141 +}; 1.2142 + 1.2143 +class DeleteDBusConnectionTask MOZ_FINAL : public Task 1.2144 +{ 1.2145 +public: 1.2146 + DeleteDBusConnectionTask() 1.2147 + { } 1.2148 + 1.2149 + void Run() 1.2150 + { 1.2151 + MOZ_ASSERT(!NS_IsMainThread()); // I/O thread 1.2152 + 1.2153 + if (!sDBusConnection) { 1.2154 + BT_WARNING("DBus connection has not been established."); 1.2155 + nsRefPtr<nsRunnable> runnable = new BluetoothService::ToggleBtAck(false); 1.2156 + if (NS_FAILED(NS_DispatchToMainThread(runnable))) { 1.2157 + BT_WARNING("Failed to dispatch to main thread!"); 1.2158 + } 1.2159 + return; 1.2160 + } 1.2161 + 1.2162 + for (uint32_t i = 0; i < ArrayLength(sBluetoothDBusSignals); ++i) { 1.2163 + dbus_bus_remove_match(sDBusConnection->GetConnection(), 1.2164 + sBluetoothDBusSignals[i], NULL); 1.2165 + } 1.2166 + 1.2167 + dbus_connection_remove_filter(sDBusConnection->GetConnection(), 1.2168 + EventFilter, nullptr); 1.2169 + 1.2170 + if (!dbus_connection_unregister_object_path(sDBusConnection->GetConnection(), 1.2171 + KEY_LOCAL_AGENT)) { 1.2172 + BT_WARNING("%s: Can't unregister object path %s for agent!", 1.2173 + __FUNCTION__, KEY_LOCAL_AGENT); 1.2174 + } 1.2175 + 1.2176 + if (!dbus_connection_unregister_object_path(sDBusConnection->GetConnection(), 1.2177 + KEY_REMOTE_AGENT)) { 1.2178 + BT_WARNING("%s: Can't unregister object path %s for agent!", 1.2179 + __FUNCTION__, KEY_REMOTE_AGENT); 1.2180 + } 1.2181 + 1.2182 + // unref stored DBusMessages before clearing the hashtable 1.2183 + sPairingReqTable->EnumerateRead(UnrefDBusMessage, nullptr); 1.2184 + sPairingReqTable->Clear(); 1.2185 + 1.2186 + sIsPairing = 0; 1.2187 + sConnectedDeviceCount = 0; 1.2188 + 1.2189 + // This command closes the DBus connection and all its instances 1.2190 + // of DBusWatch will be removed and free'd. 1.2191 + sDBusConnection = nullptr; 1.2192 + 1.2193 + // We can only dispatch to the BT thread if we're on the main 1.2194 + // thread. Thus we dispatch our runnable to the main thread 1.2195 + // from where it will forward itself to the BT thread. 1.2196 + nsRefPtr<nsRunnable> runnable = new DisableBluetoothRunnable(); 1.2197 + if (NS_FAILED(NS_DispatchToMainThread(runnable))) { 1.2198 + BT_WARNING("Failed to dispatch to BT thread!"); 1.2199 + } 1.2200 + } 1.2201 + 1.2202 +private: 1.2203 + static PLDHashOperator 1.2204 + UnrefDBusMessage(const nsAString& key, DBusMessage* value, void* arg) 1.2205 + { 1.2206 + dbus_message_unref(value); 1.2207 + return PL_DHASH_NEXT; 1.2208 + } 1.2209 +}; 1.2210 + 1.2211 +class StopBluetoothRunnable MOZ_FINAL : public nsRunnable 1.2212 +{ 1.2213 +public: 1.2214 + NS_IMETHOD Run() 1.2215 + { 1.2216 + MOZ_ASSERT(!NS_IsMainThread()); // BT thread 1.2217 + 1.2218 + // This could block. It should never be run on the main thread. 1.2219 + MonitorAutoLock lock(*sStopBluetoothMonitor); 1.2220 + if (sConnectedDeviceCount > 0) { 1.2221 + lock.Wait(PR_SecondsToInterval(TIMEOUT_FORCE_TO_DISABLE_BT)); 1.2222 + } 1.2223 + 1.2224 + DispatchToDBusThread(new DeleteDBusConnectionTask()); 1.2225 + 1.2226 + return NS_OK; 1.2227 + } 1.2228 +}; 1.2229 + 1.2230 +nsresult 1.2231 +BluetoothDBusService::StopInternal() 1.2232 +{ 1.2233 + nsRefPtr<nsRunnable> runnable = new StopBluetoothRunnable(); 1.2234 + nsresult rv = DispatchToBtThread(runnable); 1.2235 + if (NS_FAILED(rv)) { 1.2236 + BT_WARNING("Failed to dispatch to BT thread!"); 1.2237 + } 1.2238 + return rv; 1.2239 +} 1.2240 + 1.2241 +class DefaultAdapterPathReplyHandler : public DBusReplyHandler 1.2242 +{ 1.2243 +public: 1.2244 + DefaultAdapterPathReplyHandler(BluetoothReplyRunnable* aRunnable) 1.2245 + : mRunnable(aRunnable) 1.2246 + { 1.2247 + MOZ_ASSERT(mRunnable); 1.2248 + } 1.2249 + 1.2250 + void Handle(DBusMessage* aReply) MOZ_OVERRIDE 1.2251 + { 1.2252 + MOZ_ASSERT(!NS_IsMainThread()); // I/O thread 1.2253 + 1.2254 + if (!aReply || (dbus_message_get_type(aReply) == DBUS_MESSAGE_TYPE_ERROR)) { 1.2255 + const char* errStr = "Timeout in DefaultAdapterPathReplyHandler"; 1.2256 + if (aReply) { 1.2257 + errStr = dbus_message_get_error_name(aReply); 1.2258 + if (!errStr) { 1.2259 + errStr = "Bluetooth DBus Error"; 1.2260 + } 1.2261 + } 1.2262 + DispatchBluetoothReply(mRunnable, BluetoothValue(), 1.2263 + NS_ConvertUTF8toUTF16(errStr)); 1.2264 + return; 1.2265 + } 1.2266 + 1.2267 + bool success; 1.2268 + nsAutoString replyError; 1.2269 + 1.2270 + if (mAdapterPath.IsEmpty()) { 1.2271 + success = HandleDefaultAdapterPathReply(aReply, replyError); 1.2272 + } else { 1.2273 + success = HandleGetPropertiesReply(aReply, replyError); 1.2274 + } 1.2275 + 1.2276 + if (!success) { 1.2277 + DispatchBluetoothReply(mRunnable, BluetoothValue(), replyError); 1.2278 + } 1.2279 + } 1.2280 + 1.2281 +protected: 1.2282 + bool HandleDefaultAdapterPathReply(DBusMessage* aReply, 1.2283 + nsAString& aReplyError) 1.2284 + { 1.2285 + BluetoothValue value; 1.2286 + DBusError error; 1.2287 + dbus_error_init(&error); 1.2288 + 1.2289 + MOZ_ASSERT(!NS_IsMainThread()); // I/O thread 1.2290 + MOZ_ASSERT(sDBusConnection); 1.2291 + 1.2292 + UnpackObjectPathMessage(aReply, &error, value, aReplyError); 1.2293 + 1.2294 + if (!aReplyError.IsEmpty()) { 1.2295 + return false; 1.2296 + } 1.2297 + 1.2298 + mAdapterPath = value.get_nsString(); 1.2299 + 1.2300 + // Acquire another reference to this reply handler 1.2301 + nsRefPtr<DefaultAdapterPathReplyHandler> handler = this; 1.2302 + 1.2303 + bool success = sDBusConnection->SendWithReply( 1.2304 + DefaultAdapterPathReplyHandler::Callback, handler.get(), 1000, 1.2305 + BLUEZ_DBUS_BASE_IFC, 1.2306 + NS_ConvertUTF16toUTF8(mAdapterPath).get(), 1.2307 + DBUS_ADAPTER_IFACE, "GetProperties", DBUS_TYPE_INVALID); 1.2308 + 1.2309 + if (!success) { 1.2310 + aReplyError = NS_LITERAL_STRING("SendWithReply failed"); 1.2311 + return false; 1.2312 + } 1.2313 + 1.2314 + unused << handler.forget(); // picked up by callback handler 1.2315 + 1.2316 + return true; 1.2317 + } 1.2318 + 1.2319 + bool HandleGetPropertiesReply(DBusMessage* aReply, 1.2320 + nsAutoString& aReplyError) 1.2321 + { 1.2322 + BluetoothValue value; 1.2323 + DBusError error; 1.2324 + dbus_error_init(&error); 1.2325 + 1.2326 + MOZ_ASSERT(!NS_IsMainThread()); // I/O thread 1.2327 + 1.2328 + bool success = UnpackPropertiesMessage(aReply, &error, value, 1.2329 + DBUS_ADAPTER_IFACE); 1.2330 + if (!success) { 1.2331 + aReplyError = NS_ConvertUTF8toUTF16(error.message); 1.2332 + return false; 1.2333 + } 1.2334 + 1.2335 + // We have to manually attach the path to the rest of the elements 1.2336 + value.get_ArrayOfBluetoothNamedValue().AppendElement( 1.2337 + BluetoothNamedValue(NS_LITERAL_STRING("Path"), mAdapterPath)); 1.2338 + 1.2339 + // Dispatch result 1.2340 + DispatchBluetoothReply(mRunnable, value, aReplyError); 1.2341 + 1.2342 + return true; 1.2343 + } 1.2344 + 1.2345 +private: 1.2346 + nsRefPtr<BluetoothReplyRunnable> mRunnable; 1.2347 + nsString mAdapterPath; 1.2348 +}; 1.2349 + 1.2350 +class DefaultAdapterTask : public Task 1.2351 +{ 1.2352 +public: 1.2353 + DefaultAdapterTask(BluetoothReplyRunnable* aRunnable) 1.2354 + : mRunnable(aRunnable) 1.2355 + { 1.2356 + MOZ_ASSERT(mRunnable); 1.2357 + } 1.2358 + 1.2359 + void Run() MOZ_OVERRIDE 1.2360 + { 1.2361 + MOZ_ASSERT(!NS_IsMainThread()); // I/O thread 1.2362 + MOZ_ASSERT(sDBusConnection); 1.2363 + 1.2364 + nsRefPtr<DefaultAdapterPathReplyHandler> handler = 1.2365 + new DefaultAdapterPathReplyHandler(mRunnable); 1.2366 + 1.2367 + bool success = sDBusConnection->SendWithReply( 1.2368 + DefaultAdapterPathReplyHandler::Callback, 1.2369 + handler.get(), 1000, BLUEZ_DBUS_BASE_IFC, 1.2370 + "/", DBUS_MANAGER_IFACE, "DefaultAdapter", 1.2371 + DBUS_TYPE_INVALID); 1.2372 + NS_ENSURE_TRUE_VOID(success); 1.2373 + 1.2374 + unused << handler.forget(); // picked up by callback handler 1.2375 + } 1.2376 + 1.2377 +private: 1.2378 + nsRefPtr<BluetoothReplyRunnable> mRunnable; 1.2379 +}; 1.2380 + 1.2381 +nsresult 1.2382 +BluetoothDBusService::GetDefaultAdapterPathInternal( 1.2383 + BluetoothReplyRunnable* aRunnable) 1.2384 +{ 1.2385 + MOZ_ASSERT(NS_IsMainThread()); 1.2386 + 1.2387 + if (!IsReady()) { 1.2388 + NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!"); 1.2389 + DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr); 1.2390 + return NS_OK; 1.2391 + } 1.2392 + 1.2393 + Task* task = new DefaultAdapterTask(aRunnable); 1.2394 + DispatchToDBusThread(task); 1.2395 + 1.2396 + return NS_OK; 1.2397 +} 1.2398 + 1.2399 +static void 1.2400 +OnSendDiscoveryMessageReply(DBusMessage *aReply, void *aData) 1.2401 +{ 1.2402 + MOZ_ASSERT(!NS_IsMainThread()); // I/O thread 1.2403 + 1.2404 + nsAutoString errorStr; 1.2405 + 1.2406 + if (!aReply) { 1.2407 + errorStr.AssignLiteral("SendDiscovery failed"); 1.2408 + } 1.2409 + 1.2410 + nsRefPtr<BluetoothReplyRunnable> runnable = 1.2411 + dont_AddRef<BluetoothReplyRunnable>(static_cast<BluetoothReplyRunnable*>(aData)); 1.2412 + 1.2413 + DispatchBluetoothReply(runnable.get(), BluetoothValue(true), errorStr); 1.2414 +} 1.2415 + 1.2416 +class SendDiscoveryMessageTask : public Task 1.2417 +{ 1.2418 +public: 1.2419 + SendDiscoveryMessageTask(const char* aMessageName, 1.2420 + BluetoothReplyRunnable* aRunnable) 1.2421 + : mMessageName(aMessageName) 1.2422 + , mRunnable(aRunnable) 1.2423 + { 1.2424 + MOZ_ASSERT(!mMessageName.IsEmpty()); 1.2425 + MOZ_ASSERT(mRunnable); 1.2426 + } 1.2427 + 1.2428 + void Run() MOZ_OVERRIDE 1.2429 + { 1.2430 + MOZ_ASSERT(!NS_IsMainThread()); // I/O thread 1.2431 + MOZ_ASSERT(sDBusConnection); 1.2432 + MOZ_ASSERT(!sAdapterPath.IsEmpty()); 1.2433 + 1.2434 + bool success = sDBusConnection->SendWithReply( 1.2435 + OnSendDiscoveryMessageReply, 1.2436 + static_cast<void*>(mRunnable.get()), -1, 1.2437 + BLUEZ_DBUS_BASE_IFC, 1.2438 + NS_ConvertUTF16toUTF8(sAdapterPath).get(), 1.2439 + DBUS_ADAPTER_IFACE, mMessageName.get(), 1.2440 + DBUS_TYPE_INVALID); 1.2441 + NS_ENSURE_TRUE_VOID(success); 1.2442 + 1.2443 + unused << mRunnable.forget(); // picked up by callback handler 1.2444 + } 1.2445 + 1.2446 +private: 1.2447 + const nsCString mMessageName; 1.2448 + nsRefPtr<BluetoothReplyRunnable> mRunnable; 1.2449 +}; 1.2450 + 1.2451 +nsresult 1.2452 +BluetoothDBusService::SendDiscoveryMessage(const char* aMessageName, 1.2453 + BluetoothReplyRunnable* aRunnable) 1.2454 +{ 1.2455 + MOZ_ASSERT(NS_IsMainThread()); 1.2456 + MOZ_ASSERT(!sAdapterPath.IsEmpty()); 1.2457 + 1.2458 + if (!IsReady()) { 1.2459 + NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!"); 1.2460 + DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr); 1.2461 + return NS_OK; 1.2462 + } 1.2463 + 1.2464 + Task* task = new SendDiscoveryMessageTask(aMessageName, aRunnable); 1.2465 + DispatchToDBusThread(task); 1.2466 + 1.2467 + return NS_OK; 1.2468 +} 1.2469 + 1.2470 +nsresult 1.2471 +BluetoothDBusService::SendInputMessage(const nsAString& aDeviceAddress, 1.2472 + const nsAString& aMessage) 1.2473 +{ 1.2474 + DBusReplyCallback callback; 1.2475 + if (aMessage.EqualsLiteral("Connect")) { 1.2476 + callback = InputConnectCallback; 1.2477 + } else if (aMessage.EqualsLiteral("Disconnect")) { 1.2478 + callback = InputDisconnectCallback; 1.2479 + } else { 1.2480 + MOZ_ASSERT(false); 1.2481 + return NS_ERROR_FAILURE; 1.2482 + } 1.2483 + 1.2484 + MOZ_ASSERT(!sAdapterPath.IsEmpty()); 1.2485 + nsString objectPath = GetObjectPathFromAddress(sAdapterPath, aDeviceAddress); 1.2486 + return SendAsyncDBusMessage(objectPath, DBUS_INPUT_IFACE, aMessage, callback); 1.2487 +} 1.2488 + 1.2489 +class SendAsyncDBusMessageTask : public Task 1.2490 +{ 1.2491 +public: 1.2492 + SendAsyncDBusMessageTask(DBusReplyCallback aCallback, 1.2493 + BluetoothServiceClass* aServiceClass, 1.2494 + const nsACString& aObjectPath, 1.2495 + const char* aInterface, 1.2496 + const nsACString& aMessage) 1.2497 + : mCallback(aCallback) 1.2498 + , mServiceClass(aServiceClass) 1.2499 + , mObjectPath(aObjectPath) 1.2500 + , mInterface(aInterface) 1.2501 + , mMessage(aMessage) 1.2502 + { 1.2503 + MOZ_ASSERT(mServiceClass); 1.2504 + MOZ_ASSERT(!mObjectPath.IsEmpty()); 1.2505 + MOZ_ASSERT(!mInterface.IsEmpty()); 1.2506 + MOZ_ASSERT(!mMessage.IsEmpty()); 1.2507 + } 1.2508 + 1.2509 + void Run() MOZ_OVERRIDE 1.2510 + { 1.2511 + MOZ_ASSERT(!NS_IsMainThread()); // I/O thread 1.2512 + MOZ_ASSERT(sDBusConnection); 1.2513 + 1.2514 + bool success = sDBusConnection->SendWithReply( 1.2515 + mCallback, static_cast<void*>(mServiceClass), -1, 1.2516 + BLUEZ_DBUS_BASE_IFC, mObjectPath.get(), mInterface.get(), 1.2517 + mMessage.get(), DBUS_TYPE_INVALID); 1.2518 + NS_ENSURE_TRUE_VOID(success); 1.2519 + 1.2520 + mServiceClass.forget(); 1.2521 + } 1.2522 + 1.2523 +private: 1.2524 + DBusReplyCallback mCallback; 1.2525 + nsAutoPtr<BluetoothServiceClass> mServiceClass; 1.2526 + const nsCString mObjectPath; 1.2527 + const nsCString mInterface; 1.2528 + const nsCString mMessage; 1.2529 +}; 1.2530 + 1.2531 +nsresult 1.2532 +BluetoothDBusService::SendAsyncDBusMessage(const nsAString& aObjectPath, 1.2533 + const char* aInterface, 1.2534 + const nsAString& aMessage, 1.2535 + DBusReplyCallback aCallback) 1.2536 +{ 1.2537 + MOZ_ASSERT(NS_IsMainThread()); 1.2538 + MOZ_ASSERT(IsEnabled()); 1.2539 + MOZ_ASSERT(aCallback); 1.2540 + MOZ_ASSERT(!aObjectPath.IsEmpty()); 1.2541 + MOZ_ASSERT(aInterface); 1.2542 + 1.2543 + nsAutoPtr<BluetoothServiceClass> serviceClass(new BluetoothServiceClass()); 1.2544 + if (!strcmp(aInterface, DBUS_SINK_IFACE)) { 1.2545 + *serviceClass = BluetoothServiceClass::A2DP; 1.2546 + } else if (!strcmp(aInterface, DBUS_INPUT_IFACE)) { 1.2547 + *serviceClass = BluetoothServiceClass::HID; 1.2548 + } else { 1.2549 + MOZ_ASSERT(false); 1.2550 + return NS_ERROR_FAILURE; 1.2551 + } 1.2552 + 1.2553 + Task* task = new SendAsyncDBusMessageTask(aCallback, 1.2554 + serviceClass.forget(), 1.2555 + NS_ConvertUTF16toUTF8(aObjectPath), 1.2556 + aInterface, 1.2557 + NS_ConvertUTF16toUTF8(aMessage)); 1.2558 + DispatchToDBusThread(task); 1.2559 + 1.2560 + return NS_OK; 1.2561 +} 1.2562 + 1.2563 +nsresult 1.2564 +BluetoothDBusService::SendSinkMessage(const nsAString& aDeviceAddress, 1.2565 + const nsAString& aMessage) 1.2566 +{ 1.2567 + DBusReplyCallback callback; 1.2568 + if (aMessage.EqualsLiteral("Connect")) { 1.2569 + callback = SinkConnectCallback; 1.2570 + } else if (aMessage.EqualsLiteral("Disconnect")) { 1.2571 + callback = SinkDisconnectCallback; 1.2572 + } else { 1.2573 + MOZ_ASSERT(false); 1.2574 + return NS_ERROR_FAILURE; 1.2575 + } 1.2576 + 1.2577 + MOZ_ASSERT(!sAdapterPath.IsEmpty()); 1.2578 + nsString objectPath = GetObjectPathFromAddress(sAdapterPath, aDeviceAddress); 1.2579 + return SendAsyncDBusMessage(objectPath, DBUS_SINK_IFACE, aMessage, callback); 1.2580 +} 1.2581 + 1.2582 +nsresult 1.2583 +BluetoothDBusService::StopDiscoveryInternal(BluetoothReplyRunnable* aRunnable) 1.2584 +{ 1.2585 + return SendDiscoveryMessage("StopDiscovery", aRunnable); 1.2586 +} 1.2587 + 1.2588 +nsresult 1.2589 +BluetoothDBusService::StartDiscoveryInternal(BluetoothReplyRunnable* aRunnable) 1.2590 +{ 1.2591 + return SendDiscoveryMessage("StartDiscovery", aRunnable); 1.2592 +} 1.2593 + 1.2594 +class BluetoothArrayOfDevicePropertiesReplyHandler : public DBusReplyHandler 1.2595 +{ 1.2596 +public: 1.2597 + BluetoothArrayOfDevicePropertiesReplyHandler( 1.2598 + const nsTArray<nsString>& aDeviceAddresses, 1.2599 + const FilterFunc aFilterFunc, BluetoothReplyRunnable* aRunnable) 1.2600 + : mDeviceAddresses(aDeviceAddresses) 1.2601 + , mProcessedDeviceAddresses(0) 1.2602 + , mFilterFunc(aFilterFunc) 1.2603 + , mRunnable(aRunnable) 1.2604 + , mValues(InfallibleTArray<BluetoothNamedValue>()) 1.2605 + { 1.2606 + MOZ_ASSERT(mRunnable); 1.2607 + } 1.2608 + 1.2609 + void Handle(DBusMessage* aReply) MOZ_OVERRIDE 1.2610 + { 1.2611 + MOZ_ASSERT(!NS_IsMainThread()); // I/O thread 1.2612 + MOZ_ASSERT(!sAdapterPath.IsEmpty()); 1.2613 + MOZ_ASSERT(!mObjectPath.IsEmpty()); 1.2614 + MOZ_ASSERT(mProcessedDeviceAddresses < mDeviceAddresses.Length()); 1.2615 + 1.2616 + const nsTArray<nsString>::index_type i = mProcessedDeviceAddresses++; 1.2617 + 1.2618 + if (!aReply || 1.2619 + (dbus_message_get_type(aReply) == DBUS_MESSAGE_TYPE_ERROR)) { 1.2620 + BT_WARNING("Invalid DBus message"); 1.2621 + ProcessRemainingDeviceAddresses(); 1.2622 + return; 1.2623 + } 1.2624 + 1.2625 + // Get device properties from result of GetProperties 1.2626 + 1.2627 + DBusError err; 1.2628 + dbus_error_init(&err); 1.2629 + 1.2630 + BluetoothValue deviceProperties; 1.2631 + 1.2632 + bool success = UnpackPropertiesMessage(aReply, &err, deviceProperties, 1.2633 + DBUS_DEVICE_IFACE); 1.2634 + if (!success) { 1.2635 + BT_WARNING("Failed to get device properties"); 1.2636 + ProcessRemainingDeviceAddresses(); 1.2637 + return; 1.2638 + } 1.2639 + 1.2640 + InfallibleTArray<BluetoothNamedValue>& devicePropertiesArray = 1.2641 + deviceProperties.get_ArrayOfBluetoothNamedValue(); 1.2642 + 1.2643 + // We have to manually attach the path to the rest of the elements 1.2644 + devicePropertiesArray.AppendElement( 1.2645 + BluetoothNamedValue(NS_LITERAL_STRING("Path"), mObjectPath)); 1.2646 + 1.2647 + // It is possible that property Icon missed due to CoD of major 1.2648 + // class is TOY but service class is "Audio", we need to assign 1.2649 + // Icon as audio-card. This is for PTS test TC_AG_COD_BV_02_I. 1.2650 + // As HFP specification defined that 1.2651 + // service class is "Audio" can be considered as HFP AG. 1.2652 + if (!ContainsIcon(devicePropertiesArray)) { 1.2653 + for (uint32_t j = 0; j < devicePropertiesArray.Length(); ++j) { 1.2654 + BluetoothNamedValue& deviceProperty = devicePropertiesArray[j]; 1.2655 + if (deviceProperty.name().EqualsLiteral("Class")) { 1.2656 + if (HasAudioService(deviceProperty.value().get_uint32_t())) { 1.2657 + devicePropertiesArray.AppendElement( 1.2658 + BluetoothNamedValue(NS_LITERAL_STRING("Icon"), 1.2659 + NS_LITERAL_STRING("audio-card"))); 1.2660 + } 1.2661 + break; 1.2662 + } 1.2663 + } 1.2664 + } 1.2665 + 1.2666 + if (mFilterFunc(deviceProperties)) { 1.2667 + mValues.get_ArrayOfBluetoothNamedValue().AppendElement( 1.2668 + BluetoothNamedValue(mDeviceAddresses[i], deviceProperties)); 1.2669 + } 1.2670 + 1.2671 + ProcessRemainingDeviceAddresses(); 1.2672 + } 1.2673 + 1.2674 + void ProcessRemainingDeviceAddresses() 1.2675 + { 1.2676 + if (mProcessedDeviceAddresses < mDeviceAddresses.Length()) { 1.2677 + if (!SendNextGetProperties()) { 1.2678 + DispatchBluetoothReply(mRunnable, BluetoothValue(), 1.2679 + NS_LITERAL_STRING( 1.2680 + "SendNextGetProperties failed")); 1.2681 + } 1.2682 + } else { 1.2683 + // Send resulting device properties 1.2684 + DispatchBluetoothReply(mRunnable, mValues, EmptyString()); 1.2685 + } 1.2686 + } 1.2687 + 1.2688 +protected: 1.2689 + bool SendNextGetProperties() 1.2690 + { 1.2691 + MOZ_ASSERT(mProcessedDeviceAddresses < mDeviceAddresses.Length()); 1.2692 + MOZ_ASSERT(!sAdapterPath.IsEmpty()); 1.2693 + MOZ_ASSERT(sDBusConnection); 1.2694 + 1.2695 + // cache object path for reply 1.2696 + mObjectPath = GetObjectPathFromAddress(sAdapterPath, 1.2697 + mDeviceAddresses[mProcessedDeviceAddresses]); 1.2698 + 1.2699 + nsRefPtr<BluetoothArrayOfDevicePropertiesReplyHandler> handler = this; 1.2700 + 1.2701 + bool success = sDBusConnection->SendWithReply( 1.2702 + BluetoothArrayOfDevicePropertiesReplyHandler::Callback, 1.2703 + handler.get(), 1000, BLUEZ_DBUS_BASE_IFC, 1.2704 + NS_ConvertUTF16toUTF8(mObjectPath).get(), 1.2705 + DBUS_DEVICE_IFACE, "GetProperties", 1.2706 + DBUS_TYPE_INVALID); 1.2707 + 1.2708 + NS_ENSURE_TRUE(success, false); 1.2709 + 1.2710 + unused << handler.forget(); // picked up by callback handler 1.2711 + 1.2712 + return true; 1.2713 + } 1.2714 + 1.2715 +private: 1.2716 + nsString mObjectPath; 1.2717 + const nsTArray<nsString> mDeviceAddresses; 1.2718 + nsTArray<nsString>::size_type mProcessedDeviceAddresses; 1.2719 + const FilterFunc mFilterFunc; 1.2720 + nsRefPtr<BluetoothReplyRunnable> mRunnable; 1.2721 + BluetoothValue mValues; 1.2722 +}; 1.2723 + 1.2724 +class ProcessRemainingDeviceAddressesTask : public Task 1.2725 +{ 1.2726 +public: 1.2727 + ProcessRemainingDeviceAddressesTask( 1.2728 + BluetoothArrayOfDevicePropertiesReplyHandler* aHandler, 1.2729 + BluetoothReplyRunnable* aRunnable) 1.2730 + : mHandler(aHandler) 1.2731 + , mRunnable(aRunnable) 1.2732 + { 1.2733 + MOZ_ASSERT(mHandler); 1.2734 + MOZ_ASSERT(mRunnable); 1.2735 + } 1.2736 + 1.2737 + void Run() MOZ_OVERRIDE 1.2738 + { 1.2739 + MOZ_ASSERT(!NS_IsMainThread()); // I/O thread 1.2740 + 1.2741 + mHandler->ProcessRemainingDeviceAddresses(); 1.2742 + } 1.2743 + 1.2744 +private: 1.2745 + nsRefPtr<BluetoothArrayOfDevicePropertiesReplyHandler> mHandler; 1.2746 + nsRefPtr<BluetoothReplyRunnable> mRunnable; 1.2747 +}; 1.2748 + 1.2749 +nsresult 1.2750 +BluetoothDBusService::GetConnectedDevicePropertiesInternal(uint16_t aServiceUuid, 1.2751 + BluetoothReplyRunnable* aRunnable) 1.2752 +{ 1.2753 + MOZ_ASSERT(NS_IsMainThread()); 1.2754 + 1.2755 + nsAutoString errorStr; 1.2756 + BluetoothValue values = InfallibleTArray<BluetoothNamedValue>(); 1.2757 + if (!IsReady()) { 1.2758 + NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!"); 1.2759 + DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr); 1.2760 + return NS_OK; 1.2761 + } 1.2762 + 1.2763 + nsTArray<nsString> deviceAddresses; 1.2764 + BluetoothProfileManagerBase* profile = 1.2765 + BluetoothUuidHelper::GetBluetoothProfileManager(aServiceUuid); 1.2766 + if (!profile) { 1.2767 + DispatchBluetoothReply(aRunnable, values, 1.2768 + NS_LITERAL_STRING(ERR_UNKNOWN_PROFILE)); 1.2769 + return NS_OK; 1.2770 + } 1.2771 + 1.2772 + if (profile->IsConnected()) { 1.2773 + nsString address; 1.2774 + profile->GetAddress(address); 1.2775 + deviceAddresses.AppendElement(address); 1.2776 + } 1.2777 + 1.2778 + BluetoothArrayOfDevicePropertiesReplyHandler* handler = 1.2779 + new BluetoothArrayOfDevicePropertiesReplyHandler(deviceAddresses, 1.2780 + GetConnectedDevicesFilter, 1.2781 + aRunnable); 1.2782 + Task* task = new ProcessRemainingDeviceAddressesTask(handler, aRunnable); 1.2783 + DispatchToDBusThread(task); 1.2784 + 1.2785 + return NS_OK; 1.2786 +} 1.2787 + 1.2788 +nsresult 1.2789 +BluetoothDBusService::GetPairedDevicePropertiesInternal( 1.2790 + const nsTArray<nsString>& aDeviceAddresses, 1.2791 + BluetoothReplyRunnable* aRunnable) 1.2792 +{ 1.2793 + MOZ_ASSERT(NS_IsMainThread()); 1.2794 + 1.2795 + if (!IsReady()) { 1.2796 + NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!"); 1.2797 + DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr); 1.2798 + return NS_OK; 1.2799 + } 1.2800 + 1.2801 + BluetoothArrayOfDevicePropertiesReplyHandler* handler = 1.2802 + new BluetoothArrayOfDevicePropertiesReplyHandler(aDeviceAddresses, 1.2803 + GetPairedDevicesFilter, 1.2804 + aRunnable); 1.2805 + Task* task = new ProcessRemainingDeviceAddressesTask(handler, aRunnable); 1.2806 + DispatchToDBusThread(task); 1.2807 + 1.2808 + return NS_OK; 1.2809 +} 1.2810 + 1.2811 +class SetPropertyTask : public Task 1.2812 +{ 1.2813 +public: 1.2814 + SetPropertyTask(BluetoothObjectType aType, 1.2815 + const nsACString& aName, 1.2816 + BluetoothReplyRunnable* aRunnable) 1.2817 + : mType(aType) 1.2818 + , mName(aName) 1.2819 + , mRunnable(aRunnable) 1.2820 + { 1.2821 + MOZ_ASSERT(mRunnable); 1.2822 + } 1.2823 + 1.2824 + void Send(unsigned int aType, const void* aValue) 1.2825 + { 1.2826 + MOZ_ASSERT(!NS_IsMainThread()); // I/O thread 1.2827 + MOZ_ASSERT(sDBusConnection); 1.2828 + MOZ_ASSERT(!sAdapterPath.IsEmpty()); 1.2829 + 1.2830 + DBusMessage* msg = 1.2831 + dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, 1.2832 + NS_ConvertUTF16toUTF8(sAdapterPath).get(), 1.2833 + sBluetoothDBusIfaces[mType], 1.2834 + "SetProperty"); 1.2835 + if (!msg) { 1.2836 + BT_WARNING("Could not allocate D-Bus message object!"); 1.2837 + return; 1.2838 + } 1.2839 + 1.2840 + const char* name = mName.get(); 1.2841 + if (!dbus_message_append_args(msg, DBUS_TYPE_STRING, &name, 1.2842 + DBUS_TYPE_INVALID)) { 1.2843 + BT_WARNING("Couldn't append arguments to dbus message!"); 1.2844 + return; 1.2845 + } 1.2846 + 1.2847 + DBusMessageIter value_iter, iter; 1.2848 + dbus_message_iter_init_append(msg, &iter); 1.2849 + char var_type[2] = {(char)aType, '\0'}; 1.2850 + if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, 1.2851 + var_type, &value_iter) || 1.2852 + !dbus_message_iter_append_basic(&value_iter, aType, aValue) || 1.2853 + !dbus_message_iter_close_container(&iter, &value_iter)) { 1.2854 + BT_WARNING("Could not append argument to method call!"); 1.2855 + dbus_message_unref(msg); 1.2856 + return; 1.2857 + } 1.2858 + 1.2859 + // msg is unref'd as part of SendWithReply 1.2860 + bool success = sDBusConnection->SendWithReply( 1.2861 + GetVoidCallback, 1.2862 + static_cast<void*>(mRunnable), 1.2863 + 1000, msg); 1.2864 + NS_ENSURE_TRUE_VOID(success); 1.2865 + 1.2866 + unused << mRunnable.forget(); // picked up by callback handler 1.2867 + } 1.2868 + 1.2869 +private: 1.2870 + BluetoothObjectType mType; 1.2871 + const nsCString mName; 1.2872 + nsRefPtr<BluetoothReplyRunnable> mRunnable; 1.2873 +}; 1.2874 + 1.2875 +class SetUInt32PropertyTask : public SetPropertyTask 1.2876 +{ 1.2877 +public: 1.2878 + SetUInt32PropertyTask(BluetoothObjectType aType, 1.2879 + const nsACString& aName, 1.2880 + uint32_t aValue, 1.2881 + BluetoothReplyRunnable* aRunnable) 1.2882 + : SetPropertyTask(aType, aName, aRunnable) 1.2883 + , mValue(aValue) 1.2884 + { } 1.2885 + 1.2886 + void Run() MOZ_OVERRIDE 1.2887 + { 1.2888 + Send(DBUS_TYPE_UINT32, &mValue); 1.2889 + } 1.2890 + 1.2891 +private: 1.2892 + dbus_uint32_t mValue; 1.2893 +}; 1.2894 + 1.2895 +class SetStringPropertyTask : public SetPropertyTask 1.2896 +{ 1.2897 +public: 1.2898 + SetStringPropertyTask(BluetoothObjectType aType, 1.2899 + const nsACString& aName, 1.2900 + const nsACString& aValue, 1.2901 + BluetoothReplyRunnable* aRunnable) 1.2902 + : SetPropertyTask(aType, aName, aRunnable) 1.2903 + , mValue(aValue) 1.2904 + { } 1.2905 + 1.2906 + void Run() MOZ_OVERRIDE 1.2907 + { 1.2908 + const char* value = mValue.get(); 1.2909 + Send(DBUS_TYPE_STRING, &value); 1.2910 + } 1.2911 + 1.2912 +private: 1.2913 + const nsCString mValue; 1.2914 +}; 1.2915 + 1.2916 +class SetBooleanPropertyTask : public SetPropertyTask 1.2917 +{ 1.2918 +public: 1.2919 + SetBooleanPropertyTask(BluetoothObjectType aType, 1.2920 + const nsACString& aName, 1.2921 + dbus_bool_t aValue, 1.2922 + BluetoothReplyRunnable* aRunnable) 1.2923 + : SetPropertyTask(aType, aName, aRunnable) 1.2924 + , mValue(aValue) 1.2925 + { 1.2926 + } 1.2927 + 1.2928 + void Run() MOZ_OVERRIDE 1.2929 + { 1.2930 + Send(DBUS_TYPE_BOOLEAN, &mValue); 1.2931 + } 1.2932 + 1.2933 +private: 1.2934 + dbus_bool_t mValue; 1.2935 +}; 1.2936 + 1.2937 +nsresult 1.2938 +BluetoothDBusService::SetProperty(BluetoothObjectType aType, 1.2939 + const BluetoothNamedValue& aValue, 1.2940 + BluetoothReplyRunnable* aRunnable) 1.2941 +{ 1.2942 + MOZ_ASSERT(NS_IsMainThread()); 1.2943 + 1.2944 + if (!IsReady()) { 1.2945 + NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!"); 1.2946 + DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr); 1.2947 + return NS_OK; 1.2948 + } 1.2949 + 1.2950 + Task* task; 1.2951 + 1.2952 + if (aValue.value().type() == BluetoothValue::Tuint32_t) { 1.2953 + task = new SetUInt32PropertyTask(aType, 1.2954 + NS_ConvertUTF16toUTF8(aValue.name()), 1.2955 + aValue.value().get_uint32_t(), aRunnable); 1.2956 + } else if (aValue.value().type() == BluetoothValue::TnsString) { 1.2957 + task = new SetStringPropertyTask(aType, 1.2958 + NS_ConvertUTF16toUTF8(aValue.name()), 1.2959 + NS_ConvertUTF16toUTF8(aValue.value().get_nsString()), aRunnable); 1.2960 + } else if (aValue.value().type() == BluetoothValue::Tbool) { 1.2961 + task = new SetBooleanPropertyTask(aType, 1.2962 + NS_ConvertUTF16toUTF8(aValue.name()), 1.2963 + aValue.value().get_bool(), aRunnable); 1.2964 + } else { 1.2965 + BT_WARNING("Property type not handled!"); 1.2966 + return NS_ERROR_FAILURE; 1.2967 + } 1.2968 + DispatchToDBusThread(task); 1.2969 + 1.2970 + return NS_OK; 1.2971 +} 1.2972 + 1.2973 +class CreatePairedDeviceInternalTask : public Task 1.2974 +{ 1.2975 +public: 1.2976 + CreatePairedDeviceInternalTask(const nsACString& aDeviceAddress, 1.2977 + int aTimeout, 1.2978 + BluetoothReplyRunnable* aRunnable) 1.2979 + : mDeviceAddress(aDeviceAddress) 1.2980 + , mTimeout(aTimeout) 1.2981 + , mRunnable(aRunnable) 1.2982 + { 1.2983 + MOZ_ASSERT(!mDeviceAddress.IsEmpty()); 1.2984 + MOZ_ASSERT(mRunnable); 1.2985 + } 1.2986 + 1.2987 + void Run() MOZ_OVERRIDE 1.2988 + { 1.2989 + MOZ_ASSERT(!NS_IsMainThread()); // I/O thread 1.2990 + MOZ_ASSERT(sDBusConnection); 1.2991 + MOZ_ASSERT(!sAdapterPath.IsEmpty()); 1.2992 + 1.2993 + const char *deviceAddress = mDeviceAddress.get(); 1.2994 + const char *deviceAgentPath = KEY_REMOTE_AGENT; 1.2995 + const char *capabilities = B2G_AGENT_CAPABILITIES; 1.2996 + 1.2997 + // Then send CreatePairedDevice, it will register a temp device agent then 1.2998 + // unregister it after pairing process is over 1.2999 + bool success = sDBusConnection->SendWithReply( 1.3000 + GetObjectPathCallback, static_cast<void*>(mRunnable), mTimeout, 1.3001 + BLUEZ_DBUS_BASE_IFC, 1.3002 + NS_ConvertUTF16toUTF8(sAdapterPath).get(), 1.3003 + DBUS_ADAPTER_IFACE, 1.3004 + "CreatePairedDevice", 1.3005 + DBUS_TYPE_STRING, &deviceAddress, 1.3006 + DBUS_TYPE_OBJECT_PATH, &deviceAgentPath, 1.3007 + DBUS_TYPE_STRING, &capabilities, 1.3008 + DBUS_TYPE_INVALID); 1.3009 + NS_ENSURE_TRUE_VOID(success); 1.3010 + 1.3011 + unused << mRunnable.forget(); // picked up by callback handler 1.3012 + 1.3013 + /** 1.3014 + * FIXME: Bug 820274 1.3015 + * 1.3016 + * If the user turns off Bluetooth in the middle of pairing process, 1.3017 + * the callback function GetObjectPathCallback may still be called 1.3018 + * while enabling next time by dbus daemon. To prevent this from 1.3019 + * happening, added a flag to distinguish if Bluetooth has been 1.3020 + * turned off. Nevertheless, we need a check if there is a better 1.3021 + * solution. 1.3022 + * 1.3023 + * Please see Bug 818696 for more information. 1.3024 + */ 1.3025 + sIsPairing++; 1.3026 + } 1.3027 + 1.3028 +private: 1.3029 + const nsCString mDeviceAddress; 1.3030 + int mTimeout; 1.3031 + nsRefPtr<BluetoothReplyRunnable> mRunnable; 1.3032 +}; 1.3033 + 1.3034 +nsresult 1.3035 +BluetoothDBusService::CreatePairedDeviceInternal( 1.3036 + const nsAString& aDeviceAddress, 1.3037 + int aTimeout, 1.3038 + BluetoothReplyRunnable* aRunnable) 1.3039 +{ 1.3040 + Task* task = new CreatePairedDeviceInternalTask( 1.3041 + NS_ConvertUTF16toUTF8(aDeviceAddress), 1.3042 + aTimeout, aRunnable); 1.3043 + DispatchToDBusThread(task); 1.3044 + 1.3045 + return NS_OK; 1.3046 +} 1.3047 + 1.3048 +class RemoveDeviceTask : public Task 1.3049 +{ 1.3050 +public: 1.3051 + RemoveDeviceTask(const nsAString& aDeviceAddress, 1.3052 + BluetoothReplyRunnable* aRunnable) 1.3053 + : mDeviceAddress(aDeviceAddress) 1.3054 + , mRunnable(aRunnable) 1.3055 + { 1.3056 + MOZ_ASSERT(!mDeviceAddress.IsEmpty()); 1.3057 + MOZ_ASSERT(mRunnable); 1.3058 + } 1.3059 + 1.3060 + void Run() MOZ_OVERRIDE 1.3061 + { 1.3062 + MOZ_ASSERT(!NS_IsMainThread()); // I/O thread 1.3063 + MOZ_ASSERT(sDBusConnection); 1.3064 + MOZ_ASSERT(!sAdapterPath.IsEmpty()); 1.3065 + 1.3066 + nsCString deviceObjectPath = 1.3067 + NS_ConvertUTF16toUTF8(GetObjectPathFromAddress(sAdapterPath, 1.3068 + mDeviceAddress)); 1.3069 + const char* cstrDeviceObjectPath = deviceObjectPath.get(); 1.3070 + 1.3071 + bool success = sDBusConnection->SendWithReply( 1.3072 + OnRemoveDeviceReply, static_cast<void*>(mRunnable.get()), -1, 1.3073 + BLUEZ_DBUS_BASE_IFC, 1.3074 + NS_ConvertUTF16toUTF8(sAdapterPath).get(), 1.3075 + DBUS_ADAPTER_IFACE, "RemoveDevice", 1.3076 + DBUS_TYPE_OBJECT_PATH, &cstrDeviceObjectPath, 1.3077 + DBUS_TYPE_INVALID); 1.3078 + NS_ENSURE_TRUE_VOID(success); 1.3079 + 1.3080 + unused << mRunnable.forget(); // picked up by callback handler 1.3081 + } 1.3082 + 1.3083 +protected: 1.3084 + static void OnRemoveDeviceReply(DBusMessage* aReply, void* aData) 1.3085 + { 1.3086 + nsAutoString errorStr; 1.3087 + 1.3088 + if (!aReply) { 1.3089 + errorStr.AssignLiteral("RemoveDevice failed"); 1.3090 + } 1.3091 + 1.3092 + nsRefPtr<BluetoothReplyRunnable> runnable = 1.3093 + dont_AddRef<BluetoothReplyRunnable>( 1.3094 + static_cast<BluetoothReplyRunnable*>(aData)); 1.3095 + 1.3096 + DispatchBluetoothReply(runnable.get(), BluetoothValue(true), errorStr); 1.3097 + } 1.3098 + 1.3099 +private: 1.3100 + const nsString mDeviceAddress; 1.3101 + nsRefPtr<BluetoothReplyRunnable> mRunnable; 1.3102 +}; 1.3103 + 1.3104 +nsresult 1.3105 +BluetoothDBusService::RemoveDeviceInternal(const nsAString& aDeviceAddress, 1.3106 + BluetoothReplyRunnable* aRunnable) 1.3107 +{ 1.3108 + MOZ_ASSERT(NS_IsMainThread()); 1.3109 + 1.3110 + if (!IsReady()) { 1.3111 + NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!"); 1.3112 + DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr); 1.3113 + return NS_OK; 1.3114 + } 1.3115 + 1.3116 + Task* task = new RemoveDeviceTask(aDeviceAddress, aRunnable); 1.3117 + DispatchToDBusThread(task); 1.3118 + 1.3119 + return NS_OK; 1.3120 +} 1.3121 + 1.3122 +class SetPinCodeTask : public Task 1.3123 +{ 1.3124 +public: 1.3125 + SetPinCodeTask(const nsAString& aDeviceAddress, 1.3126 + const nsACString& aPinCode, 1.3127 + BluetoothReplyRunnable* aRunnable) 1.3128 + : mDeviceAddress(aDeviceAddress) 1.3129 + , mPinCode(aPinCode) 1.3130 + , mRunnable(aRunnable) 1.3131 + { 1.3132 + MOZ_ASSERT(!mDeviceAddress.IsEmpty()); 1.3133 + MOZ_ASSERT(mRunnable); 1.3134 + } 1.3135 + 1.3136 + void Run() MOZ_OVERRIDE 1.3137 + { 1.3138 + MOZ_ASSERT(!NS_IsMainThread()); // I/O thread 1.3139 + 1.3140 + nsAutoString errorStr; 1.3141 + BluetoothValue v = true; 1.3142 + DBusMessage *msg; 1.3143 + if (!sPairingReqTable->Get(mDeviceAddress, &msg)) { 1.3144 + BT_WARNING("%s: Couldn't get original request message.", __FUNCTION__); 1.3145 + errorStr.AssignLiteral("Couldn't get original request message."); 1.3146 + DispatchBluetoothReply(mRunnable, v, errorStr); 1.3147 + return; 1.3148 + } 1.3149 + 1.3150 + DBusMessage *reply = dbus_message_new_method_return(msg); 1.3151 + 1.3152 + if (!reply) { 1.3153 + BT_WARNING("%s: Memory can't be allocated for the message.", __FUNCTION__); 1.3154 + dbus_message_unref(msg); 1.3155 + errorStr.AssignLiteral("Memory can't be allocated for the message."); 1.3156 + DispatchBluetoothReply(mRunnable, v, errorStr); 1.3157 + return; 1.3158 + } 1.3159 + 1.3160 + const char* pinCode = mPinCode.get(); 1.3161 + 1.3162 + if (!dbus_message_append_args(reply, 1.3163 + DBUS_TYPE_STRING, &pinCode, 1.3164 + DBUS_TYPE_INVALID)) { 1.3165 + BT_WARNING("%s: Couldn't append arguments to dbus message.", __FUNCTION__); 1.3166 + errorStr.AssignLiteral("Couldn't append arguments to dbus message."); 1.3167 + } else { 1.3168 + MOZ_ASSERT(sDBusConnection); 1.3169 + sDBusConnection->Send(reply); 1.3170 + } 1.3171 + 1.3172 + dbus_message_unref(msg); 1.3173 + dbus_message_unref(reply); 1.3174 + 1.3175 + sPairingReqTable->Remove(mDeviceAddress); 1.3176 + DispatchBluetoothReply(mRunnable, v, errorStr); 1.3177 + } 1.3178 + 1.3179 +private: 1.3180 + const nsString mDeviceAddress; 1.3181 + const nsCString mPinCode; 1.3182 + nsRefPtr<BluetoothReplyRunnable> mRunnable; 1.3183 +}; 1.3184 + 1.3185 +bool 1.3186 +BluetoothDBusService::SetPinCodeInternal(const nsAString& aDeviceAddress, 1.3187 + const nsAString& aPinCode, 1.3188 + BluetoothReplyRunnable* aRunnable) 1.3189 +{ 1.3190 + Task* task = new SetPinCodeTask(aDeviceAddress, 1.3191 + NS_ConvertUTF16toUTF8(aPinCode), 1.3192 + aRunnable); 1.3193 + DispatchToDBusThread(task); 1.3194 + 1.3195 + return true; 1.3196 +} 1.3197 + 1.3198 +class SetPasskeyTask : public Task 1.3199 +{ 1.3200 +public: 1.3201 + SetPasskeyTask(const nsAString& aDeviceAddress, 1.3202 + uint32_t aPasskey, 1.3203 + BluetoothReplyRunnable* aRunnable) 1.3204 + : mDeviceAddress(aDeviceAddress) 1.3205 + , mPasskey(aPasskey) 1.3206 + , mRunnable(aRunnable) 1.3207 + { 1.3208 + MOZ_ASSERT(!mDeviceAddress.IsEmpty()); 1.3209 + MOZ_ASSERT(mRunnable); 1.3210 + } 1.3211 + 1.3212 + void Run() MOZ_OVERRIDE 1.3213 + { 1.3214 + MOZ_ASSERT(!NS_IsMainThread()); // I/O thread 1.3215 + 1.3216 + nsAutoString errorStr; 1.3217 + BluetoothValue v = true; 1.3218 + DBusMessage *msg; 1.3219 + if (!sPairingReqTable->Get(mDeviceAddress, &msg)) { 1.3220 + BT_WARNING("%s: Couldn't get original request message.", __FUNCTION__); 1.3221 + errorStr.AssignLiteral("Couldn't get original request message."); 1.3222 + DispatchBluetoothReply(mRunnable, v, errorStr); 1.3223 + return; 1.3224 + } 1.3225 + 1.3226 + DBusMessage *reply = dbus_message_new_method_return(msg); 1.3227 + 1.3228 + if (!reply) { 1.3229 + BT_WARNING("%s: Memory can't be allocated for the message.", __FUNCTION__); 1.3230 + dbus_message_unref(msg); 1.3231 + errorStr.AssignLiteral("Memory can't be allocated for the message."); 1.3232 + DispatchBluetoothReply(mRunnable, v, errorStr); 1.3233 + return; 1.3234 + } 1.3235 + 1.3236 + uint32_t passkey = mPasskey; 1.3237 + 1.3238 + if (!dbus_message_append_args(reply, 1.3239 + DBUS_TYPE_UINT32, &passkey, 1.3240 + DBUS_TYPE_INVALID)) { 1.3241 + BT_WARNING("%s: Couldn't append arguments to dbus message.", __FUNCTION__); 1.3242 + errorStr.AssignLiteral("Couldn't append arguments to dbus message."); 1.3243 + } else { 1.3244 + MOZ_ASSERT(sDBusConnection); 1.3245 + sDBusConnection->Send(reply); 1.3246 + } 1.3247 + 1.3248 + dbus_message_unref(msg); 1.3249 + dbus_message_unref(reply); 1.3250 + 1.3251 + sPairingReqTable->Remove(mDeviceAddress); 1.3252 + DispatchBluetoothReply(mRunnable, v, errorStr); 1.3253 + } 1.3254 + 1.3255 +private: 1.3256 + nsString mDeviceAddress; 1.3257 + uint32_t mPasskey; 1.3258 + nsRefPtr<BluetoothReplyRunnable> mRunnable; 1.3259 +}; 1.3260 + 1.3261 +bool 1.3262 +BluetoothDBusService::SetPasskeyInternal(const nsAString& aDeviceAddress, 1.3263 + uint32_t aPasskey, 1.3264 + BluetoothReplyRunnable* aRunnable) 1.3265 +{ 1.3266 + Task* task = new SetPasskeyTask(aDeviceAddress, 1.3267 + aPasskey, 1.3268 + aRunnable); 1.3269 + DispatchToDBusThread(task); 1.3270 + 1.3271 + return true; 1.3272 +} 1.3273 + 1.3274 + 1.3275 +bool 1.3276 +BluetoothDBusService::SetPairingConfirmationInternal( 1.3277 + const nsAString& aDeviceAddress, 1.3278 + bool aConfirm, 1.3279 + BluetoothReplyRunnable* aRunnable) 1.3280 +{ 1.3281 + MOZ_ASSERT(NS_IsMainThread()); 1.3282 + 1.3283 + Task* task = new SetPairingConfirmationTask(aDeviceAddress, 1.3284 + aConfirm, 1.3285 + aRunnable); 1.3286 + DispatchToDBusThread(task); 1.3287 + 1.3288 + return true; 1.3289 +} 1.3290 + 1.3291 +static void 1.3292 +NextBluetoothProfileController() 1.3293 +{ 1.3294 + MOZ_ASSERT(NS_IsMainThread()); 1.3295 + 1.3296 + // First, remove the task at the front which has been already done. 1.3297 + NS_ENSURE_FALSE_VOID(sControllerArray.IsEmpty()); 1.3298 + sControllerArray.RemoveElementAt(0); 1.3299 + 1.3300 + // Re-check if the task array is empty, if it's not, the next task will begin. 1.3301 + NS_ENSURE_FALSE_VOID(sControllerArray.IsEmpty()); 1.3302 + sControllerArray[0]->StartSession(); 1.3303 +} 1.3304 + 1.3305 +static void 1.3306 +ConnectDisconnect(bool aConnect, const nsAString& aDeviceAddress, 1.3307 + BluetoothReplyRunnable* aRunnable, 1.3308 + uint16_t aServiceUuid, uint32_t aCod = 0) 1.3309 +{ 1.3310 + MOZ_ASSERT(NS_IsMainThread()); 1.3311 + MOZ_ASSERT(aRunnable); 1.3312 + 1.3313 + BluetoothProfileController* controller = 1.3314 + new BluetoothProfileController(aConnect, aDeviceAddress, aRunnable, 1.3315 + NextBluetoothProfileController, 1.3316 + aServiceUuid, aCod); 1.3317 + sControllerArray.AppendElement(controller); 1.3318 + 1.3319 + /** 1.3320 + * If the request is the first element of the quene, start from here. Note 1.3321 + * that other request is pushed into the quene and is popped out after the 1.3322 + * first one is completed. See NextBluetoothProfileController() for details. 1.3323 + */ 1.3324 + if (sControllerArray.Length() == 1) { 1.3325 + sControllerArray[0]->StartSession(); 1.3326 + } 1.3327 +} 1.3328 + 1.3329 +void 1.3330 +BluetoothDBusService::Connect(const nsAString& aDeviceAddress, 1.3331 + uint32_t aCod, 1.3332 + uint16_t aServiceUuid, 1.3333 + BluetoothReplyRunnable* aRunnable) 1.3334 +{ 1.3335 + ConnectDisconnect(true, aDeviceAddress, aRunnable, aServiceUuid, aCod); 1.3336 +} 1.3337 + 1.3338 +void 1.3339 +BluetoothDBusService::Disconnect(const nsAString& aDeviceAddress, 1.3340 + uint16_t aServiceUuid, 1.3341 + BluetoothReplyRunnable* aRunnable) 1.3342 +{ 1.3343 + ConnectDisconnect(false, aDeviceAddress, aRunnable, aServiceUuid); 1.3344 +} 1.3345 + 1.3346 +bool 1.3347 +BluetoothDBusService::IsConnected(const uint16_t aServiceUuid) 1.3348 +{ 1.3349 + MOZ_ASSERT(NS_IsMainThread()); 1.3350 + 1.3351 + BluetoothProfileManagerBase* profile = 1.3352 + BluetoothUuidHelper::GetBluetoothProfileManager(aServiceUuid); 1.3353 + if (!profile) { 1.3354 + BT_WARNING(ERR_UNKNOWN_PROFILE); 1.3355 + return false; 1.3356 + } 1.3357 + 1.3358 + NS_ENSURE_TRUE(profile, false); 1.3359 + return profile->IsConnected(); 1.3360 +} 1.3361 + 1.3362 +#ifdef MOZ_B2G_RIL 1.3363 +void 1.3364 +BluetoothDBusService::AnswerWaitingCall(BluetoothReplyRunnable* aRunnable) 1.3365 +{ 1.3366 + MOZ_ASSERT(NS_IsMainThread()); 1.3367 + 1.3368 + BluetoothHfpManager* hfp = BluetoothHfpManager::Get(); 1.3369 + hfp->AnswerWaitingCall(); 1.3370 + 1.3371 + DispatchBluetoothReply(aRunnable, BluetoothValue(true), EmptyString()); 1.3372 +} 1.3373 + 1.3374 +void 1.3375 +BluetoothDBusService::IgnoreWaitingCall(BluetoothReplyRunnable* aRunnable) 1.3376 +{ 1.3377 + MOZ_ASSERT(NS_IsMainThread()); 1.3378 + 1.3379 + BluetoothHfpManager* hfp = BluetoothHfpManager::Get(); 1.3380 + hfp->IgnoreWaitingCall(); 1.3381 + 1.3382 + DispatchBluetoothReply(aRunnable, BluetoothValue(true), EmptyString()); 1.3383 +} 1.3384 + 1.3385 +void 1.3386 +BluetoothDBusService::ToggleCalls(BluetoothReplyRunnable* aRunnable) 1.3387 +{ 1.3388 + MOZ_ASSERT(NS_IsMainThread()); 1.3389 + 1.3390 + BluetoothHfpManager* hfp = BluetoothHfpManager::Get(); 1.3391 + hfp->ToggleCalls(); 1.3392 + 1.3393 + DispatchBluetoothReply(aRunnable, BluetoothValue(true), EmptyString()); 1.3394 +} 1.3395 +#endif // MOZ_B2G_RIL 1.3396 + 1.3397 +class OnUpdateSdpRecordsRunnable : public nsRunnable 1.3398 +{ 1.3399 +public: 1.3400 + OnUpdateSdpRecordsRunnable(const nsAString& aObjectPath, 1.3401 + BluetoothProfileManagerBase* aManager) 1.3402 + : mManager(aManager) 1.3403 + { 1.3404 + MOZ_ASSERT(!aObjectPath.IsEmpty()); 1.3405 + MOZ_ASSERT(aManager); 1.3406 + 1.3407 + mDeviceAddress = GetAddressFromObjectPath(aObjectPath); 1.3408 + } 1.3409 + 1.3410 + nsresult 1.3411 + Run() 1.3412 + { 1.3413 + MOZ_ASSERT(NS_IsMainThread()); 1.3414 + 1.3415 + mManager->OnUpdateSdpRecords(mDeviceAddress); 1.3416 + 1.3417 + return NS_OK; 1.3418 + } 1.3419 + 1.3420 +private: 1.3421 + nsString mDeviceAddress; 1.3422 + BluetoothProfileManagerBase* mManager; 1.3423 +}; 1.3424 + 1.3425 +class OnGetServiceChannelRunnable : public nsRunnable 1.3426 +{ 1.3427 +public: 1.3428 + OnGetServiceChannelRunnable(const nsAString& aDeviceAddress, 1.3429 + const nsAString& aServiceUuid, 1.3430 + int aChannel, 1.3431 + BluetoothProfileManagerBase* aManager) 1.3432 + : mDeviceAddress(aDeviceAddress) 1.3433 + , mServiceUuid(aServiceUuid) 1.3434 + , mChannel(aChannel) 1.3435 + , mManager(aManager) 1.3436 + { 1.3437 + MOZ_ASSERT(!aDeviceAddress.IsEmpty()); 1.3438 + MOZ_ASSERT(!aServiceUuid.IsEmpty()); 1.3439 + MOZ_ASSERT(aManager); 1.3440 + } 1.3441 + 1.3442 + NS_IMETHOD Run() 1.3443 + { 1.3444 + MOZ_ASSERT(NS_IsMainThread()); 1.3445 + 1.3446 + mManager->OnGetServiceChannel(mDeviceAddress, mServiceUuid, mChannel); 1.3447 + 1.3448 + return NS_OK; 1.3449 + } 1.3450 + 1.3451 +private: 1.3452 + nsString mDeviceAddress; 1.3453 + nsString mServiceUuid; 1.3454 + int mChannel; 1.3455 + BluetoothProfileManagerBase* mManager; 1.3456 +}; 1.3457 + 1.3458 +class OnGetServiceChannelReplyHandler : public DBusReplyHandler 1.3459 +{ 1.3460 +public: 1.3461 + OnGetServiceChannelReplyHandler(const nsAString& aDeviceAddress, 1.3462 + const nsAString& aServiceUUID, 1.3463 + BluetoothProfileManagerBase* aBluetoothProfileManager) 1.3464 + : mDeviceAddress(aDeviceAddress), 1.3465 + mServiceUUID(aServiceUUID), 1.3466 + mBluetoothProfileManager(aBluetoothProfileManager) 1.3467 + { 1.3468 + MOZ_ASSERT(mBluetoothProfileManager); 1.3469 + } 1.3470 + 1.3471 + void Handle(DBusMessage* aReply) 1.3472 + { 1.3473 + MOZ_ASSERT(!NS_IsMainThread()); // I/O thread 1.3474 + 1.3475 + // The default channel is an invalid value of -1. We 1.3476 + // update it if we have received a correct reply. Both 1.3477 + // cases, valid and invalid channel numbers, are handled 1.3478 + // in BluetoothProfileManagerBase::OnGetServiceChannel. 1.3479 + 1.3480 + int channel = -1; 1.3481 + 1.3482 + if (aReply && (dbus_message_get_type(aReply) != DBUS_MESSAGE_TYPE_ERROR)) { 1.3483 + channel = dbus_returns_int32(aReply); 1.3484 + } 1.3485 + 1.3486 + nsRefPtr<nsRunnable> r = new OnGetServiceChannelRunnable(mDeviceAddress, 1.3487 + mServiceUUID, 1.3488 + channel, 1.3489 + mBluetoothProfileManager); 1.3490 + nsresult rv = NS_DispatchToMainThread(r); 1.3491 + NS_ENSURE_SUCCESS_VOID(rv); 1.3492 + } 1.3493 + 1.3494 +private: 1.3495 + nsString mDeviceAddress; 1.3496 + nsString mServiceUUID; 1.3497 + BluetoothProfileManagerBase* mBluetoothProfileManager; 1.3498 +}; 1.3499 + 1.3500 +class GetServiceChannelTask : public Task 1.3501 +{ 1.3502 +public: 1.3503 + GetServiceChannelTask(const nsAString& aDeviceAddress, 1.3504 + const nsAString& aServiceUUID, 1.3505 + BluetoothProfileManagerBase* aBluetoothProfileManager) 1.3506 + : mDeviceAddress(aDeviceAddress) 1.3507 + , mServiceUUID(aServiceUUID) 1.3508 + , mBluetoothProfileManager(aBluetoothProfileManager) 1.3509 + { 1.3510 + MOZ_ASSERT(!mDeviceAddress.IsEmpty()); 1.3511 + MOZ_ASSERT(mBluetoothProfileManager); 1.3512 + } 1.3513 + 1.3514 + void Run() MOZ_OVERRIDE 1.3515 + { 1.3516 + static const int sProtocolDescriptorList = 0x0004; 1.3517 + 1.3518 + MOZ_ASSERT(!NS_IsMainThread()); // I/O thread 1.3519 + MOZ_ASSERT(sDBusConnection); 1.3520 + MOZ_ASSERT(!sAdapterPath.IsEmpty()); 1.3521 + 1.3522 + nsString objectPath = 1.3523 + GetObjectPathFromAddress(sAdapterPath, mDeviceAddress); 1.3524 + 1.3525 + nsRefPtr<OnGetServiceChannelReplyHandler> handler = 1.3526 + new OnGetServiceChannelReplyHandler(mDeviceAddress, mServiceUUID, 1.3527 + mBluetoothProfileManager); 1.3528 + 1.3529 + nsCString serviceUUID = NS_ConvertUTF16toUTF8(mServiceUUID); 1.3530 + const char* cstrServiceUUID = serviceUUID.get(); 1.3531 + 1.3532 + bool success = sDBusConnection->SendWithReply( 1.3533 + OnGetServiceChannelReplyHandler::Callback, handler, -1, 1.3534 + BLUEZ_DBUS_BASE_IFC, 1.3535 + NS_ConvertUTF16toUTF8(objectPath).get(), 1.3536 + DBUS_DEVICE_IFACE, "GetServiceAttributeValue", 1.3537 + DBUS_TYPE_STRING, &cstrServiceUUID, 1.3538 + DBUS_TYPE_UINT16, &sProtocolDescriptorList, 1.3539 + DBUS_TYPE_INVALID); 1.3540 + NS_ENSURE_TRUE_VOID(success); 1.3541 + 1.3542 + unused << handler.forget(); // picked up by callback handler 1.3543 + } 1.3544 + 1.3545 +private: 1.3546 + nsString mDeviceAddress; 1.3547 + nsString mServiceUUID; 1.3548 + BluetoothProfileManagerBase* mBluetoothProfileManager; 1.3549 +}; 1.3550 + 1.3551 +nsresult 1.3552 +BluetoothDBusService::GetServiceChannel(const nsAString& aDeviceAddress, 1.3553 + const nsAString& aServiceUUID, 1.3554 + BluetoothProfileManagerBase* aManager) 1.3555 +{ 1.3556 + MOZ_ASSERT(NS_IsMainThread()); 1.3557 + 1.3558 + if (!IsReady()) { 1.3559 + NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!"); 1.3560 + return NS_OK; 1.3561 + } 1.3562 + 1.3563 +#ifdef MOZ_WIDGET_GONK 1.3564 + // GetServiceAttributeValue only exists in android's bluez dbus binding 1.3565 + // implementation 1.3566 + Task* task = new GetServiceChannelTask(aDeviceAddress, 1.3567 + aServiceUUID, 1.3568 + aManager); 1.3569 + DispatchToDBusThread(task); 1.3570 +#else 1.3571 + // FIXME/Bug 793977 qdot: Just set something for desktop, until we have a 1.3572 + // parser for the GetServiceAttributes xml block 1.3573 + // 1.3574 + // Even though we are on the main thread already, we need to dispatch a 1.3575 + // runnable here. OnGetServiceChannel needs mRunnable to be set, which 1.3576 + // happens after GetServiceChannel returns. 1.3577 + nsRefPtr<nsRunnable> r = new OnGetServiceChannelRunnable(aDeviceAddress, 1.3578 + aServiceUUID, 1.3579 + 1, 1.3580 + aManager); 1.3581 + NS_DispatchToMainThread(r); 1.3582 +#endif 1.3583 + 1.3584 + return NS_OK; 1.3585 +} 1.3586 + 1.3587 +class UpdateSdpRecordsTask : public Task 1.3588 +{ 1.3589 +public: 1.3590 + UpdateSdpRecordsTask(const nsAString& aDeviceAddress, 1.3591 + BluetoothProfileManagerBase* aBluetoothProfileManager) 1.3592 + : mDeviceAddress(aDeviceAddress) 1.3593 + , mBluetoothProfileManager(aBluetoothProfileManager) 1.3594 + { 1.3595 + MOZ_ASSERT(!mDeviceAddress.IsEmpty()); 1.3596 + MOZ_ASSERT(mBluetoothProfileManager); 1.3597 + } 1.3598 + 1.3599 + void Run() MOZ_OVERRIDE 1.3600 + { 1.3601 + MOZ_ASSERT(!NS_IsMainThread()); // I/O thread 1.3602 + MOZ_ASSERT(sDBusConnection); 1.3603 + MOZ_ASSERT(!sAdapterPath.IsEmpty()); 1.3604 + 1.3605 + const nsString objectPath = 1.3606 + GetObjectPathFromAddress(sAdapterPath, mDeviceAddress); 1.3607 + 1.3608 + // I choose to use raw pointer here because this is going to be passed as an 1.3609 + // argument into SendWithReply() at once. 1.3610 + OnUpdateSdpRecordsRunnable* callbackRunnable = 1.3611 + new OnUpdateSdpRecordsRunnable(objectPath, mBluetoothProfileManager); 1.3612 + 1.3613 + sDBusConnection->SendWithReply(DiscoverServicesCallback, 1.3614 + (void*)callbackRunnable, -1, 1.3615 + BLUEZ_DBUS_BASE_IFC, 1.3616 + NS_ConvertUTF16toUTF8(objectPath).get(), 1.3617 + DBUS_DEVICE_IFACE, 1.3618 + "DiscoverServices", 1.3619 + DBUS_TYPE_STRING, &EmptyCString(), 1.3620 + DBUS_TYPE_INVALID); 1.3621 + } 1.3622 + 1.3623 +protected: 1.3624 + static void DiscoverServicesCallback(DBusMessage* aMsg, void* aData) 1.3625 + { 1.3626 + MOZ_ASSERT(!NS_IsMainThread()); // I/O thread 1.3627 + 1.3628 + nsRefPtr<OnUpdateSdpRecordsRunnable> r( 1.3629 + static_cast<OnUpdateSdpRecordsRunnable*>(aData)); 1.3630 + NS_DispatchToMainThread(r); 1.3631 + } 1.3632 + 1.3633 +private: 1.3634 + const nsString mDeviceAddress; 1.3635 + BluetoothProfileManagerBase* mBluetoothProfileManager; 1.3636 +}; 1.3637 + 1.3638 +bool 1.3639 +BluetoothDBusService::UpdateSdpRecords(const nsAString& aDeviceAddress, 1.3640 + BluetoothProfileManagerBase* aManager) 1.3641 +{ 1.3642 + MOZ_ASSERT(NS_IsMainThread()); 1.3643 + 1.3644 + Task* task = new UpdateSdpRecordsTask(aDeviceAddress, aManager); 1.3645 + DispatchToDBusThread(task); 1.3646 + 1.3647 + return true; 1.3648 +} 1.3649 + 1.3650 +void 1.3651 +BluetoothDBusService::SendFile(const nsAString& aDeviceAddress, 1.3652 + BlobParent* aBlobParent, 1.3653 + BlobChild* aBlobChild, 1.3654 + BluetoothReplyRunnable* aRunnable) 1.3655 +{ 1.3656 + MOZ_ASSERT(NS_IsMainThread()); 1.3657 + 1.3658 + // Currently we only support one device sending one file at a time, 1.3659 + // so we don't need aDeviceAddress here because the target device 1.3660 + // has been determined when calling 'Connect()'. Nevertheless, keep 1.3661 + // it for future use. 1.3662 + BluetoothOppManager* opp = BluetoothOppManager::Get(); 1.3663 + nsAutoString errorStr; 1.3664 + if (!opp || !opp->SendFile(aDeviceAddress, aBlobParent)) { 1.3665 + errorStr.AssignLiteral("Calling SendFile() failed"); 1.3666 + } 1.3667 + 1.3668 + DispatchBluetoothReply(aRunnable, BluetoothValue(true), errorStr); 1.3669 +} 1.3670 + 1.3671 +void 1.3672 +BluetoothDBusService::SendFile(const nsAString& aDeviceAddress, 1.3673 + nsIDOMBlob* aBlob, 1.3674 + BluetoothReplyRunnable* aRunnable) 1.3675 +{ 1.3676 + MOZ_ASSERT(NS_IsMainThread()); 1.3677 + 1.3678 + // Currently we only support one device sending one file at a time, 1.3679 + // so we don't need aDeviceAddress here because the target device 1.3680 + // has been determined when calling 'Connect()'. Nevertheless, keep 1.3681 + // it for future use. 1.3682 + BluetoothOppManager* opp = BluetoothOppManager::Get(); 1.3683 + nsAutoString errorStr; 1.3684 + if (!opp || !opp->SendFile(aDeviceAddress, aBlob)) { 1.3685 + errorStr.AssignLiteral("Calling SendFile() failed"); 1.3686 + } 1.3687 + 1.3688 + DispatchBluetoothReply(aRunnable, BluetoothValue(true), errorStr); 1.3689 +} 1.3690 + 1.3691 +void 1.3692 +BluetoothDBusService::StopSendingFile(const nsAString& aDeviceAddress, 1.3693 + BluetoothReplyRunnable* aRunnable) 1.3694 +{ 1.3695 + MOZ_ASSERT(NS_IsMainThread()); 1.3696 + 1.3697 + // Currently we only support one device sending one file at a time, 1.3698 + // so we don't need aDeviceAddress here because the target device 1.3699 + // has been determined when calling 'Connect()'. Nevertheless, keep 1.3700 + // it for future use. 1.3701 + BluetoothOppManager* opp = BluetoothOppManager::Get(); 1.3702 + nsAutoString errorStr; 1.3703 + if (!opp || !opp->StopSendingFile()) { 1.3704 + errorStr.AssignLiteral("Calling StopSendingFile() failed"); 1.3705 + } 1.3706 + 1.3707 + DispatchBluetoothReply(aRunnable, BluetoothValue(true), errorStr); 1.3708 +} 1.3709 + 1.3710 +void 1.3711 +BluetoothDBusService::ConfirmReceivingFile(const nsAString& aDeviceAddress, 1.3712 + bool aConfirm, 1.3713 + BluetoothReplyRunnable* aRunnable) 1.3714 +{ 1.3715 + MOZ_ASSERT(NS_IsMainThread(), "Must be called from main thread!"); 1.3716 + 1.3717 + // Currently we only support one device sending one file at a time, 1.3718 + // so we don't need aDeviceAddress here because the target device 1.3719 + // has been determined when calling 'Connect()'. Nevertheless, keep 1.3720 + // it for future use. 1.3721 + BluetoothOppManager* opp = BluetoothOppManager::Get(); 1.3722 + nsAutoString errorStr; 1.3723 + if (!opp || !opp->ConfirmReceivingFile(aConfirm)) { 1.3724 + errorStr.AssignLiteral("Calling ConfirmReceivingFile() failed"); 1.3725 + } 1.3726 + 1.3727 + DispatchBluetoothReply(aRunnable, BluetoothValue(true), errorStr); 1.3728 +} 1.3729 + 1.3730 +void 1.3731 +BluetoothDBusService::ConnectSco(BluetoothReplyRunnable* aRunnable) 1.3732 +{ 1.3733 + MOZ_ASSERT(NS_IsMainThread()); 1.3734 + 1.3735 + BluetoothHfpManager* hfp = BluetoothHfpManager::Get(); 1.3736 + if (!hfp || !hfp->ConnectSco(aRunnable)) { 1.3737 + NS_NAMED_LITERAL_STRING(replyError, "Calling ConnectSco() failed"); 1.3738 + DispatchBluetoothReply(aRunnable, BluetoothValue(), replyError); 1.3739 + } 1.3740 +} 1.3741 + 1.3742 +void 1.3743 +BluetoothDBusService::DisconnectSco(BluetoothReplyRunnable* aRunnable) 1.3744 +{ 1.3745 + MOZ_ASSERT(NS_IsMainThread()); 1.3746 + 1.3747 + BluetoothHfpManager* hfp = BluetoothHfpManager::Get(); 1.3748 + if (!hfp || !hfp->DisconnectSco()) { 1.3749 + NS_NAMED_LITERAL_STRING(replyError, "Calling DisconnectSco() failed"); 1.3750 + DispatchBluetoothReply(aRunnable, BluetoothValue(), replyError); 1.3751 + return; 1.3752 + } 1.3753 + 1.3754 + DispatchBluetoothReply(aRunnable, BluetoothValue(true), EmptyString()); 1.3755 +} 1.3756 + 1.3757 +void 1.3758 +BluetoothDBusService::IsScoConnected(BluetoothReplyRunnable* aRunnable) 1.3759 +{ 1.3760 + MOZ_ASSERT(NS_IsMainThread()); 1.3761 + 1.3762 + BluetoothHfpManager* hfp = BluetoothHfpManager::Get(); 1.3763 + if (!hfp) { 1.3764 + NS_NAMED_LITERAL_STRING(replyError, "Fail to get BluetoothHfpManager"); 1.3765 + DispatchBluetoothReply(aRunnable, BluetoothValue(), replyError); 1.3766 + return; 1.3767 + } 1.3768 + 1.3769 + DispatchBluetoothReply(aRunnable, hfp->IsScoConnected(), EmptyString()); 1.3770 +} 1.3771 + 1.3772 +class SendMetadataTask : public Task 1.3773 +{ 1.3774 +public: 1.3775 + SendMetadataTask(const nsAString& aDeviceAddress, 1.3776 + const nsACString& aTitle, 1.3777 + const nsACString& aArtist, 1.3778 + const nsACString& aAlbum, 1.3779 + int64_t aMediaNumber, 1.3780 + int64_t aTotalMediaCount, 1.3781 + int64_t aDuration, 1.3782 + BluetoothReplyRunnable* aRunnable) 1.3783 + : mDeviceAddress(aDeviceAddress) 1.3784 + , mTitle(aTitle) 1.3785 + , mArtist(aArtist) 1.3786 + , mAlbum(aAlbum) 1.3787 + , mMediaNumber(aMediaNumber) 1.3788 + , mTotalMediaCount(aTotalMediaCount) 1.3789 + , mDuration(aDuration) 1.3790 + , mRunnable(aRunnable) 1.3791 + { 1.3792 + MOZ_ASSERT(!mDeviceAddress.IsEmpty()); 1.3793 + MOZ_ASSERT(mRunnable); 1.3794 + } 1.3795 + 1.3796 + void Run() MOZ_OVERRIDE 1.3797 + { 1.3798 + MOZ_ASSERT(!NS_IsMainThread()); // I/O thread 1.3799 + MOZ_ASSERT(sDBusConnection); 1.3800 + MOZ_ASSERT(!sAdapterPath.IsEmpty()); 1.3801 + 1.3802 + // We currently don't support genre field in music player. 1.3803 + // In order to send media metadata through AVRCP, we set genre to an empty 1.3804 + // string to match the BlueZ method "UpdateMetaData" with signature "sssssss", 1.3805 + // which takes genre field as the last parameter. 1.3806 + nsCString tempGenre = EmptyCString(); 1.3807 + nsCString tempMediaNumber = EmptyCString(); 1.3808 + nsCString tempTotalMediaCount = EmptyCString(); 1.3809 + nsCString tempDuration = EmptyCString(); 1.3810 + 1.3811 + if (mMediaNumber >= 0) { 1.3812 + tempMediaNumber.AppendInt(mMediaNumber); 1.3813 + } 1.3814 + if (mTotalMediaCount >= 0) { 1.3815 + tempTotalMediaCount.AppendInt(mTotalMediaCount); 1.3816 + } 1.3817 + if (mDuration >= 0) { 1.3818 + tempDuration.AppendInt(mDuration); 1.3819 + } 1.3820 + 1.3821 + const nsCString objectPath = NS_ConvertUTF16toUTF8( 1.3822 + GetObjectPathFromAddress(sAdapterPath, mDeviceAddress)); 1.3823 + 1.3824 + const char* title = mTitle.get(); 1.3825 + const char* album = mAlbum.get(); 1.3826 + const char* artist = mArtist.get(); 1.3827 + const char* mediaNumber = tempMediaNumber.get(); 1.3828 + const char* totalMediaCount = tempTotalMediaCount.get(); 1.3829 + const char* duration = tempDuration.get(); 1.3830 + const char* genre = tempGenre.get(); 1.3831 + 1.3832 + bool success = sDBusConnection->SendWithReply( 1.3833 + GetVoidCallback, static_cast<void*>(mRunnable.get()), -1, 1.3834 + BLUEZ_DBUS_BASE_IFC, 1.3835 + objectPath.get(), 1.3836 + DBUS_CTL_IFACE, "UpdateMetaData", 1.3837 + DBUS_TYPE_STRING, &title, 1.3838 + DBUS_TYPE_STRING, &artist, 1.3839 + DBUS_TYPE_STRING, &album, 1.3840 + DBUS_TYPE_STRING, &mediaNumber, 1.3841 + DBUS_TYPE_STRING, &totalMediaCount, 1.3842 + DBUS_TYPE_STRING, &duration, 1.3843 + DBUS_TYPE_STRING, &genre, 1.3844 + DBUS_TYPE_INVALID); 1.3845 + NS_ENSURE_TRUE_VOID(success); 1.3846 + 1.3847 + unused << mRunnable.forget(); // picked up by callback handler 1.3848 + } 1.3849 + 1.3850 +private: 1.3851 + const nsString mDeviceAddress; 1.3852 + const nsCString mTitle; 1.3853 + const nsCString mArtist; 1.3854 + const nsCString mAlbum; 1.3855 + int64_t mMediaNumber; 1.3856 + int64_t mTotalMediaCount; 1.3857 + int64_t mDuration; 1.3858 + nsRefPtr<BluetoothReplyRunnable> mRunnable; 1.3859 +}; 1.3860 + 1.3861 +void 1.3862 +BluetoothDBusService::SendMetaData(const nsAString& aTitle, 1.3863 + const nsAString& aArtist, 1.3864 + const nsAString& aAlbum, 1.3865 + int64_t aMediaNumber, 1.3866 + int64_t aTotalMediaCount, 1.3867 + int64_t aDuration, 1.3868 + BluetoothReplyRunnable* aRunnable) 1.3869 +{ 1.3870 + MOZ_ASSERT(NS_IsMainThread()); 1.3871 + 1.3872 + if (!IsReady()) { 1.3873 + NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!"); 1.3874 + DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr); 1.3875 + return; 1.3876 + } 1.3877 + 1.3878 + BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get(); 1.3879 + NS_ENSURE_TRUE_VOID(a2dp); 1.3880 + 1.3881 + if (!a2dp->IsConnected()) { 1.3882 + DispatchBluetoothReply(aRunnable, BluetoothValue(), 1.3883 + NS_LITERAL_STRING(ERR_A2DP_IS_DISCONNECTED)); 1.3884 + return; 1.3885 + } else if (!a2dp->IsAvrcpConnected()) { 1.3886 + DispatchBluetoothReply(aRunnable, BluetoothValue(), 1.3887 + NS_LITERAL_STRING(ERR_AVRCP_IS_DISCONNECTED)); 1.3888 + return; 1.3889 + } 1.3890 + 1.3891 + nsAutoString prevTitle, prevAlbum; 1.3892 + a2dp->GetTitle(prevTitle); 1.3893 + a2dp->GetAlbum(prevAlbum); 1.3894 + 1.3895 + if (aMediaNumber != a2dp->GetMediaNumber() || 1.3896 + !aTitle.Equals(prevTitle) || 1.3897 + !aAlbum.Equals(prevAlbum)) { 1.3898 + UpdateNotification(ControlEventId::EVENT_TRACK_CHANGED, aMediaNumber); 1.3899 + } 1.3900 + 1.3901 + nsAutoString deviceAddress; 1.3902 + a2dp->GetAddress(deviceAddress); 1.3903 + 1.3904 + Task* task = new SendMetadataTask( 1.3905 + deviceAddress, 1.3906 + NS_ConvertUTF16toUTF8(aTitle), 1.3907 + NS_ConvertUTF16toUTF8(aArtist), 1.3908 + NS_ConvertUTF16toUTF8(aAlbum), 1.3909 + aMediaNumber, 1.3910 + aTotalMediaCount, 1.3911 + aDuration, 1.3912 + aRunnable); 1.3913 + DispatchToDBusThread(task); 1.3914 + 1.3915 + a2dp->UpdateMetaData(aTitle, aArtist, aAlbum, 1.3916 + aMediaNumber, aTotalMediaCount, aDuration); 1.3917 +} 1.3918 + 1.3919 +static ControlPlayStatus 1.3920 +PlayStatusStringToControlPlayStatus(const nsAString& aPlayStatus) 1.3921 +{ 1.3922 + ControlPlayStatus playStatus = ControlPlayStatus::PLAYSTATUS_UNKNOWN; 1.3923 + if (aPlayStatus.EqualsLiteral("STOPPED")) { 1.3924 + playStatus = ControlPlayStatus::PLAYSTATUS_STOPPED; 1.3925 + } else if (aPlayStatus.EqualsLiteral("PLAYING")) { 1.3926 + playStatus = ControlPlayStatus::PLAYSTATUS_PLAYING; 1.3927 + } else if (aPlayStatus.EqualsLiteral("PAUSED")) { 1.3928 + playStatus = ControlPlayStatus::PLAYSTATUS_PAUSED; 1.3929 + } else if (aPlayStatus.EqualsLiteral("FWD_SEEK")) { 1.3930 + playStatus = ControlPlayStatus::PLAYSTATUS_FWD_SEEK; 1.3931 + } else if (aPlayStatus.EqualsLiteral("REV_SEEK")) { 1.3932 + playStatus = ControlPlayStatus::PLAYSTATUS_REV_SEEK; 1.3933 + } else if (aPlayStatus.EqualsLiteral("ERROR")) { 1.3934 + playStatus = ControlPlayStatus::PLAYSTATUS_ERROR; 1.3935 + } 1.3936 + 1.3937 + return playStatus; 1.3938 +} 1.3939 + 1.3940 +class SendPlayStatusTask : public Task 1.3941 +{ 1.3942 +public: 1.3943 + SendPlayStatusTask(const nsAString& aDeviceAddress, 1.3944 + int64_t aDuration, 1.3945 + int64_t aPosition, 1.3946 + ControlPlayStatus aPlayStatus, 1.3947 + BluetoothReplyRunnable* aRunnable) 1.3948 + : mDeviceAddress(aDeviceAddress) 1.3949 + , mDuration(aDuration) 1.3950 + , mPosition(aPosition) 1.3951 + , mPlayStatus(aPlayStatus) 1.3952 + , mRunnable(aRunnable) 1.3953 + { 1.3954 + MOZ_ASSERT(!mDeviceAddress.IsEmpty()); 1.3955 + MOZ_ASSERT(mRunnable); 1.3956 + } 1.3957 + 1.3958 + void Run() MOZ_OVERRIDE 1.3959 + { 1.3960 + MOZ_ASSERT(!NS_IsMainThread()); // I/O thread 1.3961 + MOZ_ASSERT(sDBusConnection); 1.3962 + MOZ_ASSERT(!sAdapterPath.IsEmpty()); 1.3963 + 1.3964 + const nsCString objectPath = NS_ConvertUTF16toUTF8( 1.3965 + GetObjectPathFromAddress(sAdapterPath, mDeviceAddress)); 1.3966 + 1.3967 + uint32_t tempPlayStatus = mPlayStatus; 1.3968 + 1.3969 + bool success = sDBusConnection->SendWithReply( 1.3970 + GetVoidCallback, static_cast<void*>(mRunnable.get()), -1, 1.3971 + BLUEZ_DBUS_BASE_IFC, 1.3972 + objectPath.get(), 1.3973 + DBUS_CTL_IFACE, "UpdatePlayStatus", 1.3974 + DBUS_TYPE_UINT32, &mDuration, 1.3975 + DBUS_TYPE_UINT32, &mPosition, 1.3976 + DBUS_TYPE_UINT32, &tempPlayStatus, 1.3977 + DBUS_TYPE_INVALID); 1.3978 + NS_ENSURE_TRUE_VOID(success); 1.3979 + 1.3980 + unused << mRunnable.forget(); // picked up by callback handler 1.3981 + } 1.3982 + 1.3983 +private: 1.3984 + const nsString mDeviceAddress; 1.3985 + int64_t mDuration; 1.3986 + int64_t mPosition; 1.3987 + ControlPlayStatus mPlayStatus; 1.3988 + nsRefPtr<BluetoothReplyRunnable> mRunnable; 1.3989 +}; 1.3990 + 1.3991 +void 1.3992 +BluetoothDBusService::SendPlayStatus(int64_t aDuration, 1.3993 + int64_t aPosition, 1.3994 + const nsAString& aPlayStatus, 1.3995 + BluetoothReplyRunnable* aRunnable) 1.3996 +{ 1.3997 + MOZ_ASSERT(NS_IsMainThread()); 1.3998 + 1.3999 + if (!IsReady()) { 1.4000 + NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!"); 1.4001 + DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr); 1.4002 + return; 1.4003 + } 1.4004 + 1.4005 + ControlPlayStatus playStatus = 1.4006 + PlayStatusStringToControlPlayStatus(aPlayStatus); 1.4007 + if (playStatus == ControlPlayStatus::PLAYSTATUS_UNKNOWN) { 1.4008 + DispatchBluetoothReply(aRunnable, BluetoothValue(), 1.4009 + NS_LITERAL_STRING("Invalid play status")); 1.4010 + return; 1.4011 + } else if (aDuration < 0) { 1.4012 + DispatchBluetoothReply(aRunnable, BluetoothValue(), 1.4013 + NS_LITERAL_STRING("Invalid duration")); 1.4014 + return; 1.4015 + } else if (aPosition < 0) { 1.4016 + DispatchBluetoothReply(aRunnable, BluetoothValue(), 1.4017 + NS_LITERAL_STRING("Invalid position")); 1.4018 + return; 1.4019 + } 1.4020 + 1.4021 + BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get(); 1.4022 + NS_ENSURE_TRUE_VOID(a2dp); 1.4023 + 1.4024 + if (!a2dp->IsConnected()) { 1.4025 + DispatchBluetoothReply(aRunnable, BluetoothValue(), 1.4026 + NS_LITERAL_STRING(ERR_A2DP_IS_DISCONNECTED)); 1.4027 + return; 1.4028 + } else if (!a2dp->IsAvrcpConnected()) { 1.4029 + DispatchBluetoothReply(aRunnable, BluetoothValue(), 1.4030 + NS_LITERAL_STRING(ERR_AVRCP_IS_DISCONNECTED)); 1.4031 + return; 1.4032 + } 1.4033 + 1.4034 + if (playStatus != a2dp->GetPlayStatus()) { 1.4035 + UpdateNotification(ControlEventId::EVENT_PLAYBACK_STATUS_CHANGED, 1.4036 + playStatus); 1.4037 + } else if (aPosition != a2dp->GetPosition()) { 1.4038 + UpdateNotification(ControlEventId::EVENT_PLAYBACK_POS_CHANGED, aPosition); 1.4039 + } 1.4040 + 1.4041 + nsAutoString deviceAddress; 1.4042 + a2dp->GetAddress(deviceAddress); 1.4043 + 1.4044 + Task* task = new SendPlayStatusTask(deviceAddress, 1.4045 + aDuration, 1.4046 + aPosition, 1.4047 + playStatus, 1.4048 + aRunnable); 1.4049 + DispatchToDBusThread(task); 1.4050 + 1.4051 + a2dp->UpdatePlayStatus(aDuration, aPosition, playStatus); 1.4052 +} 1.4053 + 1.4054 +static void 1.4055 +ControlCallback(DBusMessage* aMsg, void* aParam) 1.4056 +{ 1.4057 + NS_ENSURE_TRUE_VOID(aMsg); 1.4058 + 1.4059 + BluetoothValue v; 1.4060 + nsAutoString replyError; 1.4061 + UnpackVoidMessage(aMsg, nullptr, v, replyError); 1.4062 + if (!v.get_bool()) { 1.4063 + BT_WARNING(NS_ConvertUTF16toUTF8(replyError).get()); 1.4064 + } 1.4065 +} 1.4066 + 1.4067 +class UpdatePlayStatusTask : public Task 1.4068 +{ 1.4069 +public: 1.4070 + UpdatePlayStatusTask(const nsAString& aDeviceAddress, 1.4071 + int32_t aDuration, 1.4072 + int32_t aPosition, 1.4073 + ControlPlayStatus aPlayStatus) 1.4074 + : mDeviceAddress(aDeviceAddress) 1.4075 + , mDuration(aDuration) 1.4076 + , mPosition(aPosition) 1.4077 + , mPlayStatus(aPlayStatus) 1.4078 + { 1.4079 + MOZ_ASSERT(!mDeviceAddress.IsEmpty()); 1.4080 + } 1.4081 + 1.4082 + void Run() MOZ_OVERRIDE 1.4083 + { 1.4084 + MOZ_ASSERT(!NS_IsMainThread()); // I/O thread 1.4085 + MOZ_ASSERT(sDBusConnection); 1.4086 + MOZ_ASSERT(!sAdapterPath.IsEmpty()); 1.4087 + 1.4088 + const nsCString objectPath = NS_ConvertUTF16toUTF8( 1.4089 + GetObjectPathFromAddress(sAdapterPath, mDeviceAddress)); 1.4090 + 1.4091 + uint32_t tempPlayStatus = mPlayStatus; 1.4092 + 1.4093 + bool success = sDBusConnection->SendWithReply( 1.4094 + ControlCallback, nullptr, -1, 1.4095 + BLUEZ_DBUS_BASE_IFC, 1.4096 + objectPath.get(), 1.4097 + DBUS_CTL_IFACE, "UpdatePlayStatus", 1.4098 + DBUS_TYPE_UINT32, &mDuration, 1.4099 + DBUS_TYPE_UINT32, &mPosition, 1.4100 + DBUS_TYPE_UINT32, &tempPlayStatus, 1.4101 + DBUS_TYPE_INVALID); 1.4102 + NS_ENSURE_TRUE_VOID(success); 1.4103 + } 1.4104 + 1.4105 +private: 1.4106 + const nsString mDeviceAddress; 1.4107 + int32_t mDuration; 1.4108 + int32_t mPosition; 1.4109 + ControlPlayStatus mPlayStatus; 1.4110 +}; 1.4111 + 1.4112 +void 1.4113 +BluetoothDBusService::UpdatePlayStatus(uint32_t aDuration, 1.4114 + uint32_t aPosition, 1.4115 + ControlPlayStatus aPlayStatus) 1.4116 +{ 1.4117 + MOZ_ASSERT(NS_IsMainThread()); 1.4118 + NS_ENSURE_TRUE_VOID(this->IsReady()); 1.4119 + 1.4120 + BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get(); 1.4121 + NS_ENSURE_TRUE_VOID(a2dp); 1.4122 + MOZ_ASSERT(a2dp->IsConnected()); 1.4123 + MOZ_ASSERT(a2dp->IsAvrcpConnected()); 1.4124 + MOZ_ASSERT(!sAdapterPath.IsEmpty()); 1.4125 + 1.4126 + nsAutoString deviceAddress; 1.4127 + a2dp->GetAddress(deviceAddress); 1.4128 + 1.4129 + Task* task = new UpdatePlayStatusTask(deviceAddress, 1.4130 + aDuration, 1.4131 + aPosition, 1.4132 + aPlayStatus); 1.4133 + DispatchToDBusThread(task); 1.4134 +} 1.4135 + 1.4136 +class UpdateNotificationTask : public Task 1.4137 +{ 1.4138 +public: 1.4139 + UpdateNotificationTask(const nsAString& aDeviceAddress, 1.4140 + BluetoothDBusService::ControlEventId aEventId, 1.4141 + uint64_t aData) 1.4142 + : mDeviceAddress(aDeviceAddress) 1.4143 + , mEventId(aEventId) 1.4144 + , mData(aData) 1.4145 + { 1.4146 + MOZ_ASSERT(!mDeviceAddress.IsEmpty()); 1.4147 + } 1.4148 + 1.4149 + void Run() MOZ_OVERRIDE 1.4150 + { 1.4151 + MOZ_ASSERT(!NS_IsMainThread()); // I/O thread 1.4152 + MOZ_ASSERT(sDBusConnection); 1.4153 + MOZ_ASSERT(!sAdapterPath.IsEmpty()); 1.4154 + 1.4155 + const nsCString objectPath = NS_ConvertUTF16toUTF8( 1.4156 + GetObjectPathFromAddress(sAdapterPath, mDeviceAddress)); 1.4157 + 1.4158 + uint16_t eventId = mEventId; 1.4159 + 1.4160 + bool success = sDBusConnection->SendWithReply( 1.4161 + ControlCallback, nullptr, -1, 1.4162 + BLUEZ_DBUS_BASE_IFC, 1.4163 + objectPath.get(), 1.4164 + DBUS_CTL_IFACE, "UpdateNotification", 1.4165 + DBUS_TYPE_UINT16, &eventId, 1.4166 + DBUS_TYPE_UINT64, &mData, 1.4167 + DBUS_TYPE_INVALID); 1.4168 + NS_ENSURE_TRUE_VOID(success); 1.4169 + } 1.4170 + 1.4171 +private: 1.4172 + const nsString mDeviceAddress; 1.4173 + int16_t mEventId; 1.4174 + int32_t mData; 1.4175 +}; 1.4176 + 1.4177 +void 1.4178 +BluetoothDBusService::UpdateNotification(ControlEventId aEventId, 1.4179 + uint64_t aData) 1.4180 +{ 1.4181 + MOZ_ASSERT(NS_IsMainThread()); 1.4182 + NS_ENSURE_TRUE_VOID(this->IsReady()); 1.4183 + 1.4184 + BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get(); 1.4185 + NS_ENSURE_TRUE_VOID(a2dp); 1.4186 + MOZ_ASSERT(a2dp->IsConnected()); 1.4187 + MOZ_ASSERT(a2dp->IsAvrcpConnected()); 1.4188 + MOZ_ASSERT(!sAdapterPath.IsEmpty()); 1.4189 + 1.4190 + nsAutoString deviceAddress; 1.4191 + a2dp->GetAddress(deviceAddress); 1.4192 + 1.4193 + Task* task = new UpdateNotificationTask(deviceAddress, aEventId, aData); 1.4194 + DispatchToDBusThread(task); 1.4195 +}