dom/bluetooth/bluez/BluetoothDBusService.cpp

changeset 0
6474c204b198
     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 +}

mercurial