dom/bluetooth/bluez/BluetoothDBusService.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
     2 /* vim: set ts=2 et sw=2 tw=80: */
     3 /*
     4 ** Copyright 2006, The Android Open Source Project
     5 **
     6 ** Licensed under the Apache License, Version 2.0 (the "License");
     7 ** you may not use this file except in compliance with the License.
     8 ** You may obtain a copy of the License at
     9 **
    10 **     http://www.apache.org/licenses/LICENSE-2.0
    11 **
    12 ** Unless required by applicable law or agreed to in writing, software
    13 ** distributed under the License is distributed on an "AS IS" BASIS,
    14 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15 ** See the License for the specific language governing permissions and
    16 ** limitations under the License.
    17 */
    19 #include "base/basictypes.h"
    20 #include "BluetoothDBusService.h"
    21 #include "BluetoothA2dpManager.h"
    22 #include "BluetoothHfpManager.h"
    23 #include "BluetoothHidManager.h"
    24 #include "BluetoothOppManager.h"
    25 #include "BluetoothProfileController.h"
    26 #include "BluetoothReplyRunnable.h"
    27 #include "BluetoothUnixSocketConnector.h"
    28 #include "BluetoothUtils.h"
    29 #include "BluetoothUuid.h"
    31 #include <cstdio>
    32 #include <dbus/dbus.h>
    34 #include "nsAutoPtr.h"
    35 #include "nsThreadUtils.h"
    36 #include "nsDebug.h"
    37 #include "nsDataHashtable.h"
    38 #include "mozilla/ArrayUtils.h"
    39 #include "mozilla/Atomics.h"
    40 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
    41 #include "mozilla/Hal.h"
    42 #include "mozilla/ipc/UnixSocket.h"
    43 #include "mozilla/ipc/DBusUtils.h"
    44 #include "mozilla/ipc/RawDBusConnection.h"
    45 #include "mozilla/LazyIdleThread.h"
    46 #include "mozilla/Mutex.h"
    47 #include "mozilla/NullPtr.h"
    48 #include "mozilla/StaticMutex.h"
    49 #include "mozilla/unused.h"
    51 #if defined(MOZ_WIDGET_GONK)
    52 #include "cutils/properties.h"
    53 #include <dlfcn.h>
    54 #endif
    56 /**
    57  * Some rules for dealing with memory in DBus:
    58  * - A DBusError only needs to be deleted if it's been set, not just
    59  *   initialized. This is why LOG_AND_FREE... is called only when an error is
    60  *   set, and the macro cleans up the error itself.
    61  * - A DBusMessage needs to be unrefed when it is newed explicitly. DBusMessages
    62  *   from signals do not need to be unrefed, as they will be cleaned by DBus
    63  *   after DBUS_HANDLER_RESULT_HANDLED is returned from the filter.
    64  */
    66 using namespace mozilla;
    67 using namespace mozilla::ipc;
    68 USING_BLUETOOTH_NAMESPACE
    70 #define B2G_AGENT_CAPABILITIES "DisplayYesNo"
    71 #define DBUS_MANAGER_IFACE BLUEZ_DBUS_BASE_IFC  ".Manager"
    72 #define DBUS_ADAPTER_IFACE BLUEZ_DBUS_BASE_IFC  ".Adapter"
    73 #define DBUS_DEVICE_IFACE  BLUEZ_DBUS_BASE_IFC  ".Device"
    74 #define DBUS_AGENT_IFACE   BLUEZ_DBUS_BASE_IFC  ".Agent"
    75 #define DBUS_SINK_IFACE    BLUEZ_DBUS_BASE_IFC  ".AudioSink"
    76 #define DBUS_CTL_IFACE     BLUEZ_DBUS_BASE_IFC  ".Control"
    77 #define DBUS_INPUT_IFACE   BLUEZ_DBUS_BASE_IFC  ".Input"
    78 #define BLUEZ_DBUS_BASE_PATH      "/org/bluez"
    79 #define BLUEZ_DBUS_BASE_IFC       "org.bluez"
    80 #define BLUEZ_ERROR_IFC           "org.bluez.Error"
    82 #define ERR_A2DP_IS_DISCONNECTED      "A2dpIsDisconnected"
    83 #define ERR_AVRCP_IS_DISCONNECTED     "AvrcpIsDisconnected"
    85 /**
    86  * To not lock Bluetooth switch button on Settings UI because of any accident,
    87  * we will force disabling Bluetooth 5 seconds after the user requesting to
    88  * turn off Bluetooth.
    89  */
    90 #define TIMEOUT_FORCE_TO_DISABLE_BT 5
    92 #define BT_LAZY_THREAD_TIMEOUT_MS 3000
    94 #ifdef MOZ_WIDGET_GONK
    95 class Bluedroid
    96 {
    97   struct ScopedDlHandleTraits
    98   {
    99     typedef void* type;
   100     static void* empty()
   101     {
   102       return nullptr;
   103     }
   104     static void release(void* handle)
   105     {
   106       if (!handle) {
   107         return;
   108       }
   109       int res = dlclose(handle);
   110       if (res) {
   111         BT_WARNING("Failed to close libbluedroid.so: %s", dlerror());
   112       }
   113     }
   114   };
   116 public:
   117   Bluedroid()
   118   : m_bt_enable(nullptr)
   119   , m_bt_disable(nullptr)
   120   , m_bt_is_enabled(nullptr)
   121   {}
   123   bool Enable()
   124   {
   125     MOZ_ASSERT(!NS_IsMainThread()); // BT thread
   127     if (!mHandle && !Init()) {
   128       return false;
   129     } else if (m_bt_is_enabled() == 1) {
   130       return true;
   131     }
   132     // 0 == success, -1 == error
   133     return !m_bt_enable();
   134   }
   136   bool Disable()
   137   {
   138     MOZ_ASSERT(!NS_IsMainThread()); // BT thread
   140     if (!IsEnabled()) {
   141       return true;
   142     }
   143     // 0 == success, -1 == error
   144     return !m_bt_disable();
   145   }
   147   bool IsEnabled() const
   148   {
   149     MOZ_ASSERT(!NS_IsMainThread()); // BT thread
   151     if (!mHandle) {
   152       return false;
   153     }
   154     // 1 == enabled, 0 == disabled, -1 == error
   155     return m_bt_is_enabled() > 0;
   156   }
   158 private:
   159   bool Init()
   160   {
   161     MOZ_ASSERT(!mHandle);
   163     Scoped<ScopedDlHandleTraits> handle(dlopen("libbluedroid.so", RTLD_LAZY));
   164     if (!handle) {
   165       BT_WARNING("Failed to open libbluedroid.so: %s", dlerror());
   166       return false;
   167     }
   168     int (*bt_enable)() = (int (*)())dlsym(handle, "bt_enable");
   169     if (!bt_enable) {
   170       BT_WARNING("Failed to lookup bt_enable: %s", dlerror());
   171       return false;
   172     }
   173     int (*bt_disable)() = (int (*)())dlsym(handle, "bt_disable");
   174     if (!bt_disable) {
   175       BT_WARNING("Failed to lookup bt_disable: %s", dlerror());
   176       return false;
   177     }
   178     int (*bt_is_enabled)() = (int (*)())dlsym(handle, "bt_is_enabled");
   179     if (!bt_is_enabled) {
   180       BT_WARNING("Failed to lookup bt_is_enabled: %s", dlerror());
   181       return false;
   182     }
   184     m_bt_enable = bt_enable;
   185     m_bt_disable = bt_disable;
   186     m_bt_is_enabled = bt_is_enabled;
   187     mHandle.reset(handle.forget());
   189     return true;
   190   }
   192   Scoped<ScopedDlHandleTraits> mHandle;
   193   int (* m_bt_enable)(void);
   194   int (* m_bt_disable)(void);
   195   int (* m_bt_is_enabled)(void);
   196 };
   198 //
   199 // BT-thread-only variables
   200 //
   201 // The variables below must only be accessed from within the BT thread.
   202 //
   204 static class Bluedroid sBluedroid;
   205 #endif
   207 //
   208 // Read-only constants
   209 //
   210 // The constants below are read-only and may be accessed from any
   211 // thread. Most of the contain DBus state or settings, so keep them
   212 // on the I/O thread if somehow possible.
   213 //
   215 typedef struct {
   216   const char* name;
   217   int type;
   218 } Properties;
   220 static const Properties sDeviceProperties[] = {
   221   {"Address", DBUS_TYPE_STRING},
   222   {"Name", DBUS_TYPE_STRING},
   223   {"Icon", DBUS_TYPE_STRING},
   224   {"Class", DBUS_TYPE_UINT32},
   225   {"UUIDs", DBUS_TYPE_ARRAY},
   226   {"Paired", DBUS_TYPE_BOOLEAN},
   227   {"Connected", DBUS_TYPE_BOOLEAN},
   228   {"Trusted", DBUS_TYPE_BOOLEAN},
   229   {"Blocked", DBUS_TYPE_BOOLEAN},
   230   {"Alias", DBUS_TYPE_STRING},
   231   {"Nodes", DBUS_TYPE_ARRAY},
   232   {"Adapter", DBUS_TYPE_OBJECT_PATH},
   233   {"LegacyPairing", DBUS_TYPE_BOOLEAN},
   234   {"RSSI", DBUS_TYPE_INT16},
   235   {"TX", DBUS_TYPE_UINT32},
   236   {"Type", DBUS_TYPE_STRING},
   237   {"Broadcaster", DBUS_TYPE_BOOLEAN},
   238   {"Services", DBUS_TYPE_ARRAY}
   239 };
   241 static const Properties sAdapterProperties[] = {
   242   {"Address", DBUS_TYPE_STRING},
   243   {"Name", DBUS_TYPE_STRING},
   244   {"Class", DBUS_TYPE_UINT32},
   245   {"Powered", DBUS_TYPE_BOOLEAN},
   246   {"Discoverable", DBUS_TYPE_BOOLEAN},
   247   {"DiscoverableTimeout", DBUS_TYPE_UINT32},
   248   {"Pairable", DBUS_TYPE_BOOLEAN},
   249   {"PairableTimeout", DBUS_TYPE_UINT32},
   250   {"Discovering", DBUS_TYPE_BOOLEAN},
   251   {"Devices", DBUS_TYPE_ARRAY},
   252   {"UUIDs", DBUS_TYPE_ARRAY},
   253   {"Type", DBUS_TYPE_STRING}
   254 };
   256 static const Properties sManagerProperties[] = {
   257   {"Adapters", DBUS_TYPE_ARRAY},
   258 };
   260 static const Properties sSinkProperties[] = {
   261   {"State", DBUS_TYPE_STRING},
   262   {"Connected", DBUS_TYPE_BOOLEAN},
   263   {"Playing", DBUS_TYPE_BOOLEAN}
   264 };
   266 static const Properties sControlProperties[] = {
   267   {"Connected", DBUS_TYPE_BOOLEAN}
   268 };
   270 static const Properties sInputProperties[] = {
   271   {"Connected", DBUS_TYPE_BOOLEAN}
   272 };
   274 static const char* const sBluetoothDBusIfaces[] = {
   275   DBUS_MANAGER_IFACE,
   276   DBUS_ADAPTER_IFACE,
   277   DBUS_DEVICE_IFACE
   278 };
   280 static const char* const sBluetoothDBusSignals[] = {
   281   "type='signal',interface='org.freedesktop.DBus'",
   282   "type='signal',interface='org.bluez.Adapter'",
   283   "type='signal',interface='org.bluez.Manager'",
   284   "type='signal',interface='org.bluez.Device'",
   285   "type='signal',interface='org.bluez.Input'",
   286   "type='signal',interface='org.bluez.Network'",
   287   "type='signal',interface='org.bluez.NetworkServer'",
   288   "type='signal',interface='org.bluez.HealthDevice'",
   289   "type='signal',interface='org.bluez.AudioSink'",
   290   "type='signal',interface='org.bluez.Control'"
   291 };
   293 // Only A2DP and HID are authorized.
   294 static const BluetoothServiceClass sAuthorizedServiceClass[] = {
   295   BluetoothServiceClass::A2DP,
   296   BluetoothServiceClass::HID
   297 };
   299 /**
   300  * The adapter name may not be ready whenever event 'AdapterAdded' is received,
   301  * so we'd like to wait for a bit. Only used on main thread.
   302  */
   303 static const int sWaitingForAdapterNameInterval = 1000; // unit: ms
   305 //
   306 // main-thread-only variables
   307 //
   308 // The variables below must be accessed from within the main thread.
   309 //
   311 // A queue for connect/disconnect request. See Bug 913372 for details.
   312 static nsTArray<nsRefPtr<BluetoothProfileController> > sControllerArray;
   314 //
   315 // I/O-thread-only variables
   316 //
   317 // The variables below must be accessed from within the I/O thread.
   318 //
   320 // The DBus connection to the BlueZ daemon
   321 static StaticAutoPtr<RawDBusConnection> sDBusConnection;
   323 // Keep the pairing requests.
   324 static unsigned int sIsPairing = 0;
   326 static nsDataHashtable<nsStringHashKey, DBusMessage* >* sPairingReqTable;
   328 // The object path of the adapter that should
   329 // be updated after switching Bluetooth.
   330 static nsString sAdapterPath;
   332 //
   333 // The variables below are currently accessed from within multiple
   334 // threads and should be moved to one specific thread if possible.
   335 //
   336 // TODO: Concurrency control is implemented by locking. Maybe there's
   337 //       a non-blocking way to implement this.
   338 //
   340 // Disconnect all profiles before turning off Bluetooth. Please see Bug 891257
   341 // for more details. |sStopBluetoothMonitor| protects access to this variable.
   342 static int sConnectedDeviceCount = 0;
   343 static StaticAutoPtr<Monitor> sStopBluetoothMonitor;
   345 // Protects against bug 969447.
   346 static StaticAutoPtr<Monitor> sGetPropertyMonitor;
   348 typedef void (*UnpackFunc)(DBusMessage*, DBusError*, BluetoothValue&, nsAString&);
   349 typedef bool (*FilterFunc)(const BluetoothValue&);
   351 static void
   352 DispatchToDBusThread(Task* task)
   353 {
   354   XRE_GetIOMessageLoop()->PostTask(FROM_HERE, task);
   355 }
   357 static nsresult
   358 DispatchToBtThread(nsIRunnable* aRunnable)
   359 {
   360   /* Due to the fact that the startup and shutdown of the Bluetooth
   361    * system can take an indefinite amount of time, a separate thread
   362    * is used for running blocking calls. The thread is not intended
   363    * for regular Bluetooth operations though.
   364    */
   365   static StaticRefPtr<LazyIdleThread> sBluetoothThread;
   367   MOZ_ASSERT(NS_IsMainThread());
   369   if (!sBluetoothThread) {
   370     sBluetoothThread = new LazyIdleThread(BT_LAZY_THREAD_TIMEOUT_MS,
   371                                           NS_LITERAL_CSTRING("BluetoothDBusService"),
   372                                           LazyIdleThread::ManualShutdown);
   373   }
   374   return sBluetoothThread->Dispatch(aRunnable, NS_DISPATCH_NORMAL);
   375 }
   377 BluetoothDBusService::BluetoothDBusService()
   378 {
   379   sGetPropertyMonitor = new Monitor("BluetoothService.sGetPropertyMonitor");
   380   sStopBluetoothMonitor = new Monitor("BluetoothService.sStopBluetoothMonitor");
   381 }
   383 BluetoothDBusService::~BluetoothDBusService()
   384 {
   385   sStopBluetoothMonitor = nullptr;
   386   sGetPropertyMonitor = nullptr;
   387 }
   389 static bool
   390 GetConnectedDevicesFilter(const BluetoothValue& aValue)
   391 {
   392   // We don't have to filter device here
   393   return true;
   394 }
   396 static bool
   397 GetPairedDevicesFilter(const BluetoothValue& aValue)
   398 {
   399   // Check property 'Paired' and only paired device will be returned
   400   if (aValue.type() != BluetoothValue::TArrayOfBluetoothNamedValue) {
   401     BT_WARNING("Not a BluetoothNamedValue array!");
   402     return false;
   403   }
   405   const InfallibleTArray<BluetoothNamedValue>& deviceProperties =
   406     aValue.get_ArrayOfBluetoothNamedValue();
   407   uint32_t length = deviceProperties.Length();
   408   for (uint32_t p = 0; p < length; ++p) {
   409     if (deviceProperties[p].name().EqualsLiteral("Paired")) {
   410       return deviceProperties[p].value().get_bool();
   411     }
   412   }
   414   return false;
   415 }
   417 class DistributeBluetoothSignalTask : public nsRunnable
   418 {
   419 public:
   420   DistributeBluetoothSignalTask(const BluetoothSignal& aSignal)
   421     : mSignal(aSignal)
   422   {
   423   }
   425   nsresult Run()
   426   {
   427     MOZ_ASSERT(NS_IsMainThread());
   429     BluetoothService* bs = BluetoothService::Get();
   430     NS_ENSURE_TRUE(bs, NS_ERROR_FAILURE);
   431     bs->DistributeSignal(mSignal);
   433     return NS_OK;
   434   }
   436 private:
   437   BluetoothSignal mSignal;
   438 };
   440 class ControlPropertyChangedHandler : public nsRunnable
   441 {
   442 public:
   443   ControlPropertyChangedHandler(const BluetoothSignal& aSignal)
   444     : mSignal(aSignal)
   445   {
   446   }
   448   nsresult Run()
   449   {
   450     MOZ_ASSERT(NS_IsMainThread());
   451     if (mSignal.value().type() != BluetoothValue::TArrayOfBluetoothNamedValue) {
   452        BT_WARNING("Wrong value type for ControlPropertyChangedHandler");
   453        return NS_ERROR_FAILURE;
   454     }
   456     InfallibleTArray<BluetoothNamedValue>& arr =
   457       mSignal.value().get_ArrayOfBluetoothNamedValue();
   458     MOZ_ASSERT(arr[0].name().EqualsLiteral("Connected"));
   459     MOZ_ASSERT(arr[0].value().type() == BluetoothValue::Tbool);
   460     bool connected = arr[0].value().get_bool();
   462     BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get();
   463     NS_ENSURE_TRUE(a2dp, NS_ERROR_FAILURE);
   464     a2dp->SetAvrcpConnected(connected);
   465     return NS_OK;
   466   }
   468 private:
   469   BluetoothSignal mSignal;
   470 };
   472 class SinkPropertyChangedHandler : public nsRunnable
   473 {
   474 public:
   475   SinkPropertyChangedHandler(const BluetoothSignal& aSignal)
   476     : mSignal(aSignal)
   477   {
   478   }
   480   NS_IMETHOD
   481   Run()
   482   {
   483     MOZ_ASSERT(NS_IsMainThread());
   484     MOZ_ASSERT(mSignal.name().EqualsLiteral("PropertyChanged"));
   485     MOZ_ASSERT(mSignal.value().type() ==
   486                BluetoothValue::TArrayOfBluetoothNamedValue);
   488     // Replace object path with device address
   489     nsString address = GetAddressFromObjectPath(mSignal.path());
   490     mSignal.path() = address;
   492     BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get();
   493     NS_ENSURE_TRUE(a2dp, NS_ERROR_FAILURE);
   494     a2dp->HandleSinkPropertyChanged(mSignal);
   495     return NS_OK;
   496   }
   498 private:
   499   BluetoothSignal mSignal;
   500 };
   502 class InputPropertyChangedHandler : public nsRunnable
   503 {
   504 public:
   505   InputPropertyChangedHandler(const BluetoothSignal& aSignal)
   506     : mSignal(aSignal)
   507   {
   508   }
   510   NS_IMETHOD
   511   Run()
   512   {
   513     MOZ_ASSERT(NS_IsMainThread());
   514     MOZ_ASSERT(mSignal.name().EqualsLiteral("PropertyChanged"));
   515     MOZ_ASSERT(mSignal.value().type() == BluetoothValue::TArrayOfBluetoothNamedValue);
   517     // Replace object path with device address
   518     nsString address = GetAddressFromObjectPath(mSignal.path());
   519     mSignal.path() = address;
   521     BluetoothHidManager* hid = BluetoothHidManager::Get();
   522     NS_ENSURE_TRUE(hid, NS_ERROR_FAILURE);
   523     hid->HandleInputPropertyChanged(mSignal);
   524     return NS_OK;
   525   }
   527 private:
   528   BluetoothSignal mSignal;
   529 };
   531 class TryFiringAdapterAddedTask : public Task
   532 {
   533 public:
   534   void Run() MOZ_OVERRIDE
   535   {
   536     MOZ_ASSERT(NS_IsMainThread());
   538     BluetoothService* bs = BluetoothService::Get();
   539     NS_ENSURE_TRUE_VOID(bs);
   541     bs->AdapterAddedReceived();
   542     bs->TryFiringAdapterAdded();
   543   }
   544 };
   546 class TryFiringAdapterAddedRunnable : public nsRunnable
   547 {
   548 public:
   549   TryFiringAdapterAddedRunnable(bool aDelay)
   550     : mDelay(aDelay)
   551   { }
   553   nsresult Run()
   554   {
   555     MOZ_ASSERT(NS_IsMainThread());
   557     if (mDelay) {
   558       MessageLoop::current()->
   559         PostDelayedTask(FROM_HERE, new TryFiringAdapterAddedTask(),
   560                         sWaitingForAdapterNameInterval);
   561     } else {
   562       MessageLoop::current()->
   563         PostTask(FROM_HERE, new TryFiringAdapterAddedTask());
   564     }
   566     return NS_OK;
   567   }
   569 private:
   570   bool mDelay;
   571 };
   573 static bool
   574 IsDBusMessageError(DBusMessage* aMsg, DBusError* aErr, nsAString& aErrorStr)
   575 {
   576   if (aErr && dbus_error_is_set(aErr)) {
   577     aErrorStr = NS_ConvertUTF8toUTF16(aErr->message);
   578     LOG_AND_FREE_DBUS_ERROR(aErr);
   579     return true;
   580   }
   582   DBusError err;
   583   dbus_error_init(&err);
   584   if (dbus_message_get_type(aMsg) == DBUS_MESSAGE_TYPE_ERROR) {
   585     const char* error_msg;
   586     if (!dbus_message_get_args(aMsg, &err, DBUS_TYPE_STRING,
   587                                &error_msg, DBUS_TYPE_INVALID) ||
   588         !error_msg) {
   589       if (dbus_error_is_set(&err)) {
   590         aErrorStr = NS_ConvertUTF8toUTF16(err.message);
   591         LOG_AND_FREE_DBUS_ERROR(&err);
   592         return true;
   593       } else {
   594         aErrorStr.AssignLiteral("Unknown Error");
   595         return true;
   596       }
   597     } else {
   598       aErrorStr = NS_ConvertUTF8toUTF16(error_msg);
   599       return true;
   600     }
   601   }
   602   return false;
   603 }
   605 static void
   606 UnpackObjectPathMessage(DBusMessage* aMsg, DBusError* aErr,
   607                         BluetoothValue& aValue, nsAString& aErrorStr)
   608 {
   609   DBusError err;
   610   dbus_error_init(&err);
   611   if (!IsDBusMessageError(aMsg, aErr, aErrorStr)) {
   612     MOZ_ASSERT(dbus_message_get_type(aMsg) == DBUS_MESSAGE_TYPE_METHOD_RETURN,
   613                "Got dbus callback that's not a METHOD_RETURN!");
   614     const char* object_path;
   615     if (!dbus_message_get_args(aMsg, &err, DBUS_TYPE_OBJECT_PATH,
   616                                &object_path, DBUS_TYPE_INVALID) ||
   617         !object_path) {
   618       if (dbus_error_is_set(&err)) {
   619         aErrorStr = NS_ConvertUTF8toUTF16(err.message);
   620         LOG_AND_FREE_DBUS_ERROR(&err);
   621       }
   622     } else {
   623       aValue = NS_ConvertUTF8toUTF16(object_path);
   624     }
   625   }
   626 }
   628 class PrepareProfileManagersRunnable : public nsRunnable
   629 {
   630 public:
   631   nsresult Run()
   632   {
   633     BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
   634     if (!hfp || !hfp->Listen()) {
   635       BT_WARNING("Failed to start listening for BluetoothHfpManager!");
   636       return NS_ERROR_FAILURE;
   637     }
   639     BluetoothOppManager* opp = BluetoothOppManager::Get();
   640     if (!opp || !opp->Listen()) {
   641       BT_WARNING("Failed to start listening for BluetoothOppManager!");
   642       return NS_ERROR_FAILURE;
   643     }
   645     BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get();
   646     NS_ENSURE_TRUE(a2dp, NS_ERROR_FAILURE);
   647     a2dp->Reset();
   649     return NS_OK;
   650   }
   651 };
   653 static void
   654 RunDBusCallback(DBusMessage* aMsg, void* aBluetoothReplyRunnable,
   655                 UnpackFunc aFunc)
   656 {
   657 #ifdef MOZ_WIDGET_GONK
   658   // Due to the fact that we're running two dbus loops on desktop implicitly by
   659   // being gtk based, sometimes we'll get signals/reply coming in on the main
   660   // thread. There's not a lot we can do about that for the time being and it
   661   // (technically) shouldn't hurt anything. However, on gonk, die.
   662   MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
   663 #endif
   664   nsRefPtr<BluetoothReplyRunnable> replyRunnable =
   665     dont_AddRef(static_cast< BluetoothReplyRunnable* >(aBluetoothReplyRunnable));
   667   MOZ_ASSERT(replyRunnable, "Callback reply runnable is null!");
   669   nsAutoString replyError;
   670   BluetoothValue v;
   671   aFunc(aMsg, nullptr, v, replyError);
   673   // Bug 941462. When blueZ replys 'I/O error', we treat it as 'internal error'.
   674   // This usually happned when the first pairing request has not yet finished,
   675   // the second pairing request issued immediately.
   676   if (replyError.EqualsLiteral("I/O error")) {
   677     replyError.AssignLiteral(ERR_INTERNAL_ERROR);
   678   }
   680   DispatchBluetoothReply(replyRunnable, v, replyError);
   681 }
   683 static void
   684 GetObjectPathCallback(DBusMessage* aMsg, void* aBluetoothReplyRunnable)
   685 {
   686   if (sIsPairing) {
   687     RunDBusCallback(aMsg, aBluetoothReplyRunnable,
   688                     UnpackObjectPathMessage);
   689     sIsPairing--;
   690   }
   691 }
   693 static void
   694 UnpackVoidMessage(DBusMessage* aMsg, DBusError* aErr, BluetoothValue& aValue,
   695                   nsAString& aErrorStr)
   696 {
   697   DBusError err;
   698   dbus_error_init(&err);
   699   if (!IsDBusMessageError(aMsg, aErr, aErrorStr) &&
   700       dbus_message_get_type(aMsg) == DBUS_MESSAGE_TYPE_METHOD_RETURN &&
   701       !dbus_message_get_args(aMsg, &err, DBUS_TYPE_INVALID)) {
   702     if (dbus_error_is_set(&err)) {
   703       aErrorStr = NS_ConvertUTF8toUTF16(err.message);
   704       LOG_AND_FREE_DBUS_ERROR(&err);
   705     }
   706   }
   707   aValue = aErrorStr.IsEmpty();
   708 }
   710 static void
   711 GetVoidCallback(DBusMessage* aMsg, void* aBluetoothReplyRunnable)
   712 {
   713   RunDBusCallback(aMsg, aBluetoothReplyRunnable,
   714                   UnpackVoidMessage);
   715 }
   717 class ReplyErrorToProfileManager : public nsRunnable
   718 {
   719 public:
   720   ReplyErrorToProfileManager(BluetoothServiceClass aServiceClass,
   721                              bool aConnect,
   722                              const nsAString& aErrorString)
   723     : mServiceClass(aServiceClass)
   724     , mConnect(aConnect)
   725     , mErrorString(aErrorString)
   726   {
   727     MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
   728   }
   730   nsresult Run()
   731   {
   732     MOZ_ASSERT(NS_IsMainThread());
   734     BluetoothProfileManagerBase* profile;
   735     if (mServiceClass == BluetoothServiceClass::HID) {
   736       profile = BluetoothHidManager::Get();
   737     } else if (mServiceClass == BluetoothServiceClass::A2DP) {
   738       profile = BluetoothA2dpManager::Get();
   739     } else {
   740       MOZ_ASSERT(false);
   741       return NS_ERROR_FAILURE;
   742     }
   744     if (mConnect) {
   745       profile->OnConnect(mErrorString);
   746     } else {
   747       profile->OnDisconnect(mErrorString);
   748     }
   750     return NS_OK;
   751   }
   753 private:
   754   BluetoothServiceClass mServiceClass;
   755   bool mConnect;
   756   nsString mErrorString;
   757 };
   759 static void
   760 CheckDBusReply(DBusMessage* aMsg, void* aServiceClass, bool aConnect)
   761 {
   762   MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
   764   NS_ENSURE_TRUE_VOID(aMsg);
   766   BluetoothValue v;
   767   nsAutoString replyError;
   768   UnpackVoidMessage(aMsg, nullptr, v, replyError);
   770   nsAutoPtr<BluetoothServiceClass> serviceClass(
   771     static_cast<BluetoothServiceClass*>(aServiceClass));
   773   if (!replyError.IsEmpty()) {
   774     NS_DispatchToMainThread(
   775       new ReplyErrorToProfileManager(*serviceClass, aConnect, replyError));
   776   }
   777 }
   779 static void
   780 InputConnectCallback(DBusMessage* aMsg, void* aParam)
   781 {
   782   CheckDBusReply(aMsg, aParam, true);
   783 }
   785 static void
   786 InputDisconnectCallback(DBusMessage* aMsg, void* aParam)
   787 {
   788   CheckDBusReply(aMsg, aParam, false);
   789 }
   791 static void
   792 SinkConnectCallback(DBusMessage* aMsg, void* aParam)
   793 {
   794   CheckDBusReply(aMsg, aParam, true);
   795 }
   797 static void
   798 SinkDisconnectCallback(DBusMessage* aMsg, void* aParam)
   799 {
   800   CheckDBusReply(aMsg, aParam, false);
   801 }
   803 static bool
   804 HasAudioService(uint32_t aCodValue)
   805 {
   806   return ((aCodValue & 0x200000) == 0x200000);
   807 }
   809 static bool
   810 ContainsIcon(const InfallibleTArray<BluetoothNamedValue>& aProperties)
   811 {
   812   for (uint8_t i = 0; i < aProperties.Length(); i++) {
   813     if (aProperties[i].name().EqualsLiteral("Icon")) {
   814       return true;
   815     }
   816   }
   817   return false;
   818 }
   820 static bool
   821 GetProperty(DBusMessageIter aIter, const Properties* aPropertyTypes,
   822             int aPropertyTypeLen, int* aPropIndex,
   823             InfallibleTArray<BluetoothNamedValue>& aProperties)
   824 {
   825   /**
   826    * Ensure GetProperty runs in critical section otherwise
   827    * crash due to timing issue occurs when BT is enabled.
   828    *
   829    * TODO: Revise GetProperty to solve the crash
   830    */
   831   MonitorAutoLock lock(*sGetPropertyMonitor);
   833   DBusMessageIter prop_val, array_val_iter;
   834   char* property = nullptr;
   835   uint32_t array_type;
   836   int i, expectedType, receivedType;
   838   if (dbus_message_iter_get_arg_type(&aIter) != DBUS_TYPE_STRING) {
   839     return false;
   840   }
   842   dbus_message_iter_get_basic(&aIter, &property);
   844   if (!dbus_message_iter_next(&aIter) ||
   845       dbus_message_iter_get_arg_type(&aIter) != DBUS_TYPE_VARIANT) {
   846     return false;
   847   }
   849   for (i = 0; i < aPropertyTypeLen; i++) {
   850     if (!strncmp(property, aPropertyTypes[i].name, strlen(property))) {
   851       break;
   852     }
   853   }
   855   if (i == aPropertyTypeLen) {
   856     BT_LOGR("unknown property: %s", property);
   857     return false;
   858   }
   860   nsAutoString propertyName;
   861   propertyName.AssignASCII(aPropertyTypes[i].name);
   862   *aPropIndex = i;
   864   // Preprocessing
   865   dbus_message_iter_recurse(&aIter, &prop_val);
   866   expectedType = aPropertyTypes[*aPropIndex].type;
   867   receivedType = dbus_message_iter_get_arg_type(&prop_val);
   869   /**
   870    * Bug 857896. Since device property "Connected" could be a boolean value or
   871    * an 2-byte array, we need to check the value type here and convert the
   872    * first byte into a boolean manually.
   873    */
   874   bool convert = false;
   875   if (propertyName.EqualsLiteral("Connected") &&
   876       receivedType == DBUS_TYPE_ARRAY) {
   877     MOZ_ASSERT(aPropertyTypes == sDeviceProperties);
   878     convert = true;
   879   }
   881   if ((receivedType != expectedType) && !convert) {
   882     BT_WARNING("Iterator not type we expect! Property name: %s,"
   883       "Property Type Expected: %d, Property Type Received: %d",
   884       NS_ConvertUTF16toUTF8(propertyName).get(), expectedType, receivedType);
   885     return false;
   886   }
   888   // Extract data
   889   BluetoothValue propertyValue;
   890   switch (receivedType) {
   891     case DBUS_TYPE_STRING:
   892     case DBUS_TYPE_OBJECT_PATH:
   893       const char* c;
   894       dbus_message_iter_get_basic(&prop_val, &c);
   895       propertyValue = NS_ConvertUTF8toUTF16(c);
   896       break;
   897     case DBUS_TYPE_UINT32:
   898     case DBUS_TYPE_INT16:
   899       uint32_t i;
   900       dbus_message_iter_get_basic(&prop_val, &i);
   901       propertyValue = i;
   902       break;
   903     case DBUS_TYPE_BOOLEAN:
   904       bool b;
   905       dbus_message_iter_get_basic(&prop_val, &b);
   906       propertyValue = b;
   907       break;
   908     case DBUS_TYPE_ARRAY:
   909       dbus_message_iter_recurse(&prop_val, &array_val_iter);
   910       array_type = dbus_message_iter_get_arg_type(&array_val_iter);
   911       if (array_type == DBUS_TYPE_OBJECT_PATH ||
   912           array_type == DBUS_TYPE_STRING) {
   913         InfallibleTArray<nsString> arr;
   914         do {
   915           const char* tmp;
   916           dbus_message_iter_get_basic(&array_val_iter, &tmp);
   917           nsAutoString s;
   918           s = NS_ConvertUTF8toUTF16(tmp);
   919           arr.AppendElement(s);
   920         } while (dbus_message_iter_next(&array_val_iter));
   921         propertyValue = arr;
   922       } else if (array_type == DBUS_TYPE_BYTE) {
   923         InfallibleTArray<uint8_t> arr;
   924         do {
   925           uint8_t tmp;
   926           dbus_message_iter_get_basic(&array_val_iter, &tmp);
   927           arr.AppendElement(tmp);
   928         } while (dbus_message_iter_next(&array_val_iter));
   929         propertyValue = arr;
   930       } else {
   931         // This happens when the array is 0-length. Apparently we get a
   932         // DBUS_TYPE_INVALID type.
   933         propertyValue = InfallibleTArray<nsString>();
   934       }
   935       break;
   936     default:
   937       NS_NOTREACHED("Cannot find dbus message type!");
   938   }
   940   // Postprocessing
   941   if (convert) {
   942     MOZ_ASSERT(propertyValue.type() == BluetoothValue::TArrayOfuint8_t);
   944     bool b = propertyValue.get_ArrayOfuint8_t()[0];
   945     propertyValue = BluetoothValue(b);
   946   } else if (propertyName.EqualsLiteral("Devices")) {
   947     MOZ_ASSERT(aPropertyTypes == sAdapterProperties);
   948     MOZ_ASSERT(propertyValue.type() == BluetoothValue::TArrayOfnsString);
   950     uint32_t length = propertyValue.get_ArrayOfnsString().Length();
   951     for (uint32_t i= 0; i < length; i++) {
   952       nsString& data = propertyValue.get_ArrayOfnsString()[i];
   953       data = GetAddressFromObjectPath(data);
   954     }
   955   }
   957   aProperties.AppendElement(BluetoothNamedValue(propertyName, propertyValue));
   958   return true;
   959 }
   961 static void
   962 ParseProperties(DBusMessageIter* aIter,
   963                 BluetoothValue& aValue,
   964                 nsAString& aErrorStr,
   965                 const Properties* aPropertyTypes,
   966                 const int aPropertyTypeLen)
   967 {
   968   DBusMessageIter dict_entry, dict;
   969   int prop_index = -1;
   971   MOZ_ASSERT(dbus_message_iter_get_arg_type(aIter) == DBUS_TYPE_ARRAY,
   972              "Trying to parse a property from sth. that's not an array");
   974   dbus_message_iter_recurse(aIter, &dict);
   975   InfallibleTArray<BluetoothNamedValue> props;
   976   do {
   977     MOZ_ASSERT(dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY,
   978                "Trying to parse a property from sth. that's not an dict!");
   979     dbus_message_iter_recurse(&dict, &dict_entry);
   981     if (!GetProperty(dict_entry, aPropertyTypes, aPropertyTypeLen, &prop_index,
   982                      props)) {
   983       aErrorStr.AssignLiteral("Can't Create Property!");
   984       BT_WARNING("Can't create property!");
   985       return;
   986     }
   987   } while (dbus_message_iter_next(&dict));
   989   aValue = props;
   990 }
   992 static bool
   993 UnpackPropertiesMessage(DBusMessage* aMsg, DBusError* aErr,
   994                         BluetoothValue& aValue, const char* aIface)
   995 {
   996   MOZ_ASSERT(aMsg);
   998   const Properties* propertyTypes;
   999   int propertyTypesLength;
  1001   nsAutoString errorStr;
  1002   if (IsDBusMessageError(aMsg, aErr, errorStr) ||
  1003       dbus_message_get_type(aMsg) != DBUS_MESSAGE_TYPE_METHOD_RETURN) {
  1004     BT_WARNING("dbus message has an error.");
  1005     return false;
  1008   DBusMessageIter iter;
  1009   if (!dbus_message_iter_init(aMsg, &iter)) {
  1010     BT_WARNING("Cannot create dbus message iter!");
  1011     return false;
  1014   if (!strcmp(aIface, DBUS_DEVICE_IFACE)) {
  1015     propertyTypes = sDeviceProperties;
  1016     propertyTypesLength = ArrayLength(sDeviceProperties);
  1017   } else if (!strcmp(aIface, DBUS_ADAPTER_IFACE)) {
  1018     propertyTypes = sAdapterProperties;
  1019     propertyTypesLength = ArrayLength(sAdapterProperties);
  1020   } else if (!strcmp(aIface, DBUS_MANAGER_IFACE)) {
  1021     propertyTypes = sManagerProperties;
  1022     propertyTypesLength = ArrayLength(sManagerProperties);
  1023   } else {
  1024     return false;
  1027   ParseProperties(&iter, aValue, errorStr, propertyTypes,
  1028                   propertyTypesLength);
  1029   return true;
  1032 static void
  1033 ParsePropertyChange(DBusMessage* aMsg, BluetoothValue& aValue,
  1034                     nsAString& aErrorStr, const Properties* aPropertyTypes,
  1035                     const int aPropertyTypeLen)
  1037   DBusMessageIter iter;
  1038   DBusError err;
  1039   int prop_index = -1;
  1040   InfallibleTArray<BluetoothNamedValue> props;
  1042   dbus_error_init(&err);
  1043   if (!dbus_message_iter_init(aMsg, &iter)) {
  1044     BT_WARNING("Can't create iterator!");
  1045     return;
  1048   if (!GetProperty(iter, aPropertyTypes, aPropertyTypeLen,
  1049                    &prop_index, props)) {
  1050     BT_WARNING("Can't get property!");
  1051     aErrorStr.AssignLiteral("Can't get property!");
  1052     return;
  1054   aValue = props;
  1057 class AppendDeviceNameReplyHandler: public DBusReplyHandler
  1059 public:
  1060   AppendDeviceNameReplyHandler(const nsCString& aIface,
  1061                                const nsString& aDevicePath,
  1062                                const BluetoothSignal& aSignal)
  1063     : mIface(aIface)
  1064     , mDevicePath(aDevicePath)
  1065     , mSignal(aSignal)
  1067     MOZ_ASSERT(!mIface.IsEmpty());
  1068     MOZ_ASSERT(!mDevicePath.IsEmpty());
  1071   void Handle(DBusMessage* aReply) MOZ_OVERRIDE
  1073     MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
  1075     if (!aReply || (dbus_message_get_type(aReply) == DBUS_MESSAGE_TYPE_ERROR)) {
  1076       return;
  1079     // Get device properties from result of GetProperties
  1081     DBusError err;
  1082     dbus_error_init(&err);
  1084     BluetoothValue deviceProperties;
  1086     bool success = UnpackPropertiesMessage(aReply, &err, deviceProperties,
  1087                                            mIface.get());
  1088     if (!success) {
  1089       BT_WARNING("Failed to get device properties");
  1090       return;
  1093     // First we replace object path with device address.
  1095     InfallibleTArray<BluetoothNamedValue>& parameters =
  1096       mSignal.value().get_ArrayOfBluetoothNamedValue();
  1097     nsString address =
  1098       GetAddressFromObjectPath(mDevicePath);
  1099     parameters[0].name().AssignLiteral("address");
  1100     parameters[0].value() = address;
  1102     // Then we append the device's name to the original signal's data.
  1104     InfallibleTArray<BluetoothNamedValue>& properties =
  1105       deviceProperties.get_ArrayOfBluetoothNamedValue();
  1106     uint32_t i;
  1107     for (i = 0; i < properties.Length(); i++) {
  1108       if (properties[i].name().EqualsLiteral("Name")) {
  1109         properties[i].name().AssignLiteral("name");
  1110         parameters.AppendElement(properties[i]);
  1111         break;
  1114     MOZ_ASSERT_IF(i == properties.Length(), "failed to get device name");
  1116     nsRefPtr<DistributeBluetoothSignalTask> task =
  1117       new DistributeBluetoothSignalTask(mSignal);
  1118     NS_DispatchToMainThread(task);
  1121 private:
  1122   nsCString mIface;
  1123   nsString mDevicePath;
  1124   BluetoothSignal mSignal;
  1125 };
  1127 static void
  1128 AppendDeviceName(BluetoothSignal& aSignal)
  1130   MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
  1131   MOZ_ASSERT(sDBusConnection);
  1133   BluetoothValue v = aSignal.value();
  1134   if (v.type() != BluetoothValue::TArrayOfBluetoothNamedValue ||
  1135       v.get_ArrayOfBluetoothNamedValue().Length() == 0) {
  1136     BT_WARNING("Invalid argument type for AppendDeviceNameRunnable");
  1137     return;
  1139   const InfallibleTArray<BluetoothNamedValue>& arr =
  1140     v.get_ArrayOfBluetoothNamedValue();
  1142   // Device object path should be put in the first element
  1143   if (!arr[0].name().EqualsLiteral("path") ||
  1144        arr[0].value().type() != BluetoothValue::TnsString) {
  1145     BT_WARNING("Invalid object path for AppendDeviceNameRunnable");
  1146     return;
  1149   nsString devicePath = arr[0].value().get_nsString();
  1151   nsRefPtr<AppendDeviceNameReplyHandler> handler =
  1152     new AppendDeviceNameReplyHandler(nsCString(DBUS_DEVICE_IFACE),
  1153                                      devicePath, aSignal);
  1155   bool success = sDBusConnection->SendWithReply(
  1156     AppendDeviceNameReplyHandler::Callback, handler.get(), 1000,
  1157     BLUEZ_DBUS_BASE_IFC, NS_ConvertUTF16toUTF8(devicePath).get(),
  1158     DBUS_DEVICE_IFACE, "GetProperties", DBUS_TYPE_INVALID);
  1160   NS_ENSURE_TRUE_VOID(success);
  1162   unused << handler.forget(); // picked up by callback handler
  1165 class SetPairingConfirmationTask : public Task
  1167 public:
  1168   SetPairingConfirmationTask(const nsAString& aDeviceAddress,
  1169                              bool aConfirm,
  1170                              BluetoothReplyRunnable* aRunnable)
  1171     : mDeviceAddress(aDeviceAddress)
  1172     , mConfirm(aConfirm)
  1173     , mRunnable(aRunnable)
  1175     MOZ_ASSERT(!mDeviceAddress.IsEmpty());
  1178   void Run() MOZ_OVERRIDE
  1180     MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
  1181     MOZ_ASSERT(sDBusConnection);
  1183     nsAutoString errorStr;
  1184     BluetoothValue v = true;
  1185     DBusMessage *msg;
  1187     if (!sPairingReqTable->Get(mDeviceAddress, &msg) && mRunnable) {
  1188       BT_WARNING("%s: Couldn't get original request message.", __FUNCTION__);
  1189       errorStr.AssignLiteral("Couldn't get original request message.");
  1190       DispatchBluetoothReply(mRunnable, v, errorStr);
  1192       return;
  1195     DBusMessage *reply;
  1197     if (mConfirm) {
  1198       reply = dbus_message_new_method_return(msg);
  1199     } else {
  1200       reply = dbus_message_new_error(msg, "org.bluez.Error.Rejected",
  1201                                      "User rejected confirmation");
  1204     if (!reply) {
  1205       BT_WARNING("%s: Memory can't be allocated for the message.", __FUNCTION__);
  1206       dbus_message_unref(msg);
  1207       errorStr.AssignLiteral("Memory can't be allocated for the message.");
  1208       if (mRunnable) {
  1209         DispatchBluetoothReply(mRunnable, v, errorStr);
  1211       return;
  1214     bool result = sDBusConnection->Send(reply);
  1215     if (!result) {
  1216       errorStr.AssignLiteral("Can't send message!");
  1219     dbus_message_unref(msg);
  1220     dbus_message_unref(reply);
  1221     sPairingReqTable->Remove(mDeviceAddress);
  1222     if (mRunnable) {
  1223       DispatchBluetoothReply(mRunnable, v, errorStr);
  1227 private:
  1228   nsString mDeviceAddress;
  1229   bool mConfirm;
  1230   nsRefPtr<BluetoothReplyRunnable> mRunnable;
  1231 };
  1233 static DBusHandlerResult
  1234 AgentEventFilter(DBusConnection *conn, DBusMessage *msg, void *data)
  1236   if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_METHOD_CALL) {
  1237     BT_WARNING("%s: agent handler not interested (not a method call).\n",
  1238                __FUNCTION__);
  1239     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
  1242   DBusError err;
  1243   dbus_error_init(&err);
  1245   BT_LOGD("%s: %s, %s", __FUNCTION__,
  1246                        dbus_message_get_path(msg),
  1247                        dbus_message_get_member(msg));
  1249   nsString signalPath = NS_ConvertUTF8toUTF16(dbus_message_get_path(msg));
  1250   nsString signalName = NS_ConvertUTF8toUTF16(dbus_message_get_member(msg));
  1251   nsString errorStr;
  1252   BluetoothValue v;
  1253   InfallibleTArray<BluetoothNamedValue> parameters;
  1254   bool isPairingReq = false;
  1255   BluetoothSignal signal(signalName, signalPath, v);
  1256   char *objectPath;
  1258   // The following descriptions of each signal are retrieved from:
  1259   //
  1260   // http://maemo.org/api_refs/5.0/beta/bluez/agent.html
  1261   //
  1262   if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, "Cancel")) {
  1263     // This method gets called to indicate that the agent request failed before
  1264     // a reply was returned.
  1266     // Return directly
  1267     DBusMessage *reply = dbus_message_new_method_return(msg);
  1269     if (!reply) {
  1270       errorStr.AssignLiteral("Memory can't be allocated for the message.");
  1271       goto handle_error;
  1274     dbus_connection_send(conn, reply, nullptr);
  1275     dbus_message_unref(reply);
  1276     v = parameters;
  1277   } else if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, "Authorize")) {
  1278     // This method gets called when the service daemon needs to authorize a
  1279     // connection/service request.
  1280     const char *uuid;
  1281     if (!dbus_message_get_args(msg, nullptr,
  1282                                DBUS_TYPE_OBJECT_PATH, &objectPath,
  1283                                DBUS_TYPE_STRING, &uuid,
  1284                                DBUS_TYPE_INVALID)) {
  1285       errorStr.AssignLiteral("Invalid arguments for Authorize() method");
  1286       goto handle_error;
  1289     NS_ConvertUTF8toUTF16 uuidStr(uuid);
  1290     BluetoothServiceClass serviceClass =
  1291       BluetoothUuidHelper::GetBluetoothServiceClass(uuidStr);
  1292     if (serviceClass == BluetoothServiceClass::UNKNOWN) {
  1293       errorStr.AssignLiteral("Failed to get service class");
  1294       goto handle_error;
  1297     DBusMessage* reply = nullptr;
  1298     uint32_t i;
  1299     for (i = 0; i < MOZ_ARRAY_LENGTH(sAuthorizedServiceClass); i++) {
  1300       if (serviceClass == sAuthorizedServiceClass[i]) {
  1301         reply = dbus_message_new_method_return(msg);
  1302         break;
  1306     // The uuid isn't authorized
  1307     if (i == MOZ_ARRAY_LENGTH(sAuthorizedServiceClass)) {
  1308       BT_WARNING("Uuid is not authorized.");
  1309       reply = dbus_message_new_error(msg, "org.bluez.Error.Rejected",
  1310                                      "The uuid is not authorized");
  1313     if (!reply) {
  1314       errorStr.AssignLiteral("Memory can't be allocated for the message.");
  1315       goto handle_error;
  1318     dbus_connection_send(conn, reply, nullptr);
  1319     dbus_message_unref(reply);
  1320     return DBUS_HANDLER_RESULT_HANDLED;
  1321   } else if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE,
  1322                                          "RequestConfirmation")) {
  1323     // This method gets called when the service daemon needs to confirm a
  1324     // passkey for an authentication.
  1325     uint32_t passkey;
  1326     if (!dbus_message_get_args(msg, nullptr,
  1327                                DBUS_TYPE_OBJECT_PATH, &objectPath,
  1328                                DBUS_TYPE_UINT32, &passkey,
  1329                                DBUS_TYPE_INVALID)) {
  1330       errorStr.AssignLiteral("Invalid arguments: RequestConfirmation()");
  1331       goto handle_error;
  1334     parameters.AppendElement(
  1335       BluetoothNamedValue(NS_LITERAL_STRING("path"),
  1336                           NS_ConvertUTF8toUTF16(objectPath)));
  1337     parameters.AppendElement(
  1338       BluetoothNamedValue(NS_LITERAL_STRING("method"),
  1339                           NS_LITERAL_STRING("confirmation")));
  1340     parameters.AppendElement(
  1341       BluetoothNamedValue(NS_LITERAL_STRING("passkey"), passkey));
  1343     v = parameters;
  1344     isPairingReq = true;
  1345   } else if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE,
  1346                                          "RequestPinCode")) {
  1347     // This method gets called when the service daemon needs to get the passkey
  1348     // for an authentication. The return value should be a string of 1-16
  1349     // characters length. The string can be alphanumeric.
  1350     if (!dbus_message_get_args(msg, nullptr,
  1351                                DBUS_TYPE_OBJECT_PATH, &objectPath,
  1352                                DBUS_TYPE_INVALID)) {
  1353       errorStr.AssignLiteral("Invalid arguments for RequestPinCode() method");
  1354       goto handle_error;
  1357     parameters.AppendElement(
  1358       BluetoothNamedValue(NS_LITERAL_STRING("path"),
  1359                           NS_ConvertUTF8toUTF16(objectPath)));
  1360     parameters.AppendElement(
  1361       BluetoothNamedValue(NS_LITERAL_STRING("method"),
  1362                           NS_LITERAL_STRING("pincode")));
  1364     v = parameters;
  1365     isPairingReq = true;
  1366   } else if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE,
  1367                                          "RequestPasskey")) {
  1368     // This method gets called when the service daemon needs to get the passkey
  1369     // for an authentication. The return value should be a numeric value
  1370     // between 0-999999.
  1371     if (!dbus_message_get_args(msg, nullptr,
  1372                                DBUS_TYPE_OBJECT_PATH, &objectPath,
  1373                                DBUS_TYPE_INVALID)) {
  1374       errorStr.AssignLiteral("Invalid arguments for RequestPasskey() method");
  1375       goto handle_error;
  1378     parameters.AppendElement(BluetoothNamedValue(
  1379                                NS_LITERAL_STRING("path"),
  1380                                NS_ConvertUTF8toUTF16(objectPath)));
  1381     parameters.AppendElement(BluetoothNamedValue(
  1382                                NS_LITERAL_STRING("method"),
  1383                                NS_LITERAL_STRING("passkey")));
  1385     v = parameters;
  1386     isPairingReq = true;
  1387   } else if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, "Release")) {
  1388     // This method gets called when the service daemon unregisters the agent.
  1389     // An agent can use it to do cleanup tasks. There is no need to unregister
  1390     // the agent, because when this method gets called it has already been
  1391     // unregistered.
  1392     DBusMessage *reply = dbus_message_new_method_return(msg);
  1394     if (!reply) {
  1395       errorStr.AssignLiteral("Memory can't be allocated for the message.");
  1396       goto handle_error;
  1399     dbus_connection_send(conn, reply, nullptr);
  1400     dbus_message_unref(reply);
  1402     // Do not send a notification to upper layer, too annoying.
  1403     return DBUS_HANDLER_RESULT_HANDLED;
  1404   } else if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, "RequestPairingConsent")) {
  1405     // Directly SetPairingconfirmation for RequestPairingConsent here
  1406     if (!dbus_message_get_args(msg, nullptr,
  1407                                DBUS_TYPE_OBJECT_PATH, &objectPath,
  1408                                DBUS_TYPE_INVALID)) {
  1409       errorStr.AssignLiteral("Invalid arguments: RequestPairingConsent()");
  1410       goto handle_error;
  1413     nsString address = GetAddressFromObjectPath(NS_ConvertUTF8toUTF16(objectPath));
  1414     sPairingReqTable->Put(address, msg);
  1415     Task* task = new SetPairingConfirmationTask(address, true, nullptr);
  1416     DispatchToDBusThread(task);
  1417     // Increase dbus message reference counts, it will be decreased in
  1418     // SetPairingConfirmationTask
  1419     dbus_message_ref(msg);
  1420     // Do not send a notification to upper layer
  1421     return DBUS_HANDLER_RESULT_HANDLED;
  1422   } else {
  1423 #ifdef DEBUG
  1424     BT_WARNING("agent handler %s: Unhandled event. Ignore.", __FUNCTION__);
  1425 #endif
  1426     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
  1429   if (!errorStr.IsEmpty()) {
  1430     BT_WARNING(NS_ConvertUTF16toUTF8(errorStr).get());
  1431     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
  1434   // Update value after parsing DBus message
  1435   signal.value() = v;
  1437   if (isPairingReq) {
  1438     sPairingReqTable->Put(
  1439       GetAddressFromObjectPath(NS_ConvertUTF8toUTF16(objectPath)), msg);
  1441     // Increase ref count here because we need this message later.
  1442     // It'll be unrefed when set*Internal() is called.
  1443     dbus_message_ref(msg);
  1445     AppendDeviceName(signal);
  1446   } else {
  1447     NS_DispatchToMainThread(new DistributeBluetoothSignalTask(signal));
  1450   return DBUS_HANDLER_RESULT_HANDLED;
  1452 handle_error:
  1453   BT_WARNING(NS_ConvertUTF16toUTF8(errorStr).get());
  1454   return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
  1457 class RegisterAgentReplyHandler : public DBusReplyHandler
  1459 public:
  1460   RegisterAgentReplyHandler(const DBusObjectPathVTable* aAgentVTable)
  1461     : mAgentVTable(aAgentVTable)
  1463     MOZ_ASSERT(aAgentVTable);
  1466   void Handle(DBusMessage* aReply)
  1468     MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
  1469     MOZ_ASSERT(sDBusConnection);
  1471     if (!aReply || (dbus_message_get_type(aReply) == DBUS_MESSAGE_TYPE_ERROR)) {
  1472       return;
  1475     // There is no "RegisterAgent" function defined in device interface.
  1476     // When we call "CreatePairedDevice", it will do device agent registration
  1477     // for us. (See maemo.org/api_refs/5.0/beta/bluez/adapter.html)
  1478     if (!dbus_connection_register_object_path(sDBusConnection->GetConnection(),
  1479                                               KEY_REMOTE_AGENT,
  1480                                               mAgentVTable,
  1481                                               nullptr)) {
  1482       BT_WARNING("%s: Can't register object path %s for remote device agent!",
  1483                  __FUNCTION__, KEY_REMOTE_AGENT);
  1484       return;
  1487     NS_DispatchToMainThread(new PrepareProfileManagersRunnable());
  1490 private:
  1491   const DBusObjectPathVTable* mAgentVTable;
  1492 };
  1494 class AddReservedServiceRecordsReplyHandler : public DBusReplyHandler
  1496 public:
  1497   void Handle(DBusMessage* aReply)
  1499     static const DBusObjectPathVTable sAgentVTable = {
  1500       nullptr, AgentEventFilter, nullptr, nullptr, nullptr, nullptr
  1501     };
  1503     MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
  1505     if (!aReply || (dbus_message_get_type(aReply) == DBUS_MESSAGE_TYPE_ERROR)) {
  1506       return;
  1509     // TODO/qdot: This needs to be held for the life of the bluetooth connection
  1510     // so we could clean it up. For right now though, we can throw it away.
  1511     nsTArray<uint32_t> handles;
  1513     ExtractHandles(aReply, handles);
  1515     if(!RegisterAgent(&sAgentVTable)) {
  1516       BT_WARNING("Failed to register agent");
  1520 private:
  1521   void ExtractHandles(DBusMessage *aMessage, nsTArray<uint32_t>& aOutHandles)
  1523     DBusError error;
  1524     int length;
  1525     uint32_t* handles = nullptr;
  1527     dbus_error_init(&error);
  1529     bool success = dbus_message_get_args(aMessage, &error,
  1530                                          DBUS_TYPE_ARRAY,
  1531                                          DBUS_TYPE_UINT32,
  1532                                          &handles, &length,
  1533                                          DBUS_TYPE_INVALID);
  1534     if (success != TRUE) {
  1535       LOG_AND_FREE_DBUS_ERROR(&error);
  1536       return;
  1539     if (!handles) {
  1540       BT_WARNING("Null array in extract_handles");
  1541       return;
  1544     for (int i = 0; i < length; ++i) {
  1545       aOutHandles.AppendElement(handles[i]);
  1549   bool RegisterAgent(const DBusObjectPathVTable* aAgentVTable)
  1551     const char* agentPath = KEY_LOCAL_AGENT;
  1552     const char* capabilities = B2G_AGENT_CAPABILITIES;
  1554     MOZ_ASSERT(sDBusConnection);
  1556     // Local agent means agent for Adapter, not agent for Device. Some signals
  1557     // will be passed to local agent, some will be passed to device agent.
  1558     // For example, if a remote device would like to pair with us, then the
  1559     // signal will be passed to local agent. If we start pairing process with
  1560     // calling CreatePairedDevice, we'll get signal which should be passed to
  1561     // device agent.
  1562     if (!dbus_connection_register_object_path(sDBusConnection->GetConnection(),
  1563                                               KEY_LOCAL_AGENT,
  1564                                               aAgentVTable,
  1565                                               nullptr)) {
  1566       BT_WARNING("%s: Can't register object path %s for agent!",
  1567                  __FUNCTION__, KEY_LOCAL_AGENT);
  1568       return false;
  1571     nsRefPtr<RegisterAgentReplyHandler> handler =
  1572       new RegisterAgentReplyHandler(aAgentVTable);
  1573     MOZ_ASSERT(!sAdapterPath.IsEmpty());
  1575     bool success = sDBusConnection->SendWithReply(
  1576       RegisterAgentReplyHandler::Callback, handler.get(), -1,
  1577       BLUEZ_DBUS_BASE_IFC,
  1578       NS_ConvertUTF16toUTF8(sAdapterPath).get(),
  1579       DBUS_ADAPTER_IFACE, "RegisterAgent",
  1580       DBUS_TYPE_OBJECT_PATH, &agentPath,
  1581       DBUS_TYPE_STRING, &capabilities,
  1582       DBUS_TYPE_INVALID);
  1584     NS_ENSURE_TRUE(success, false);
  1586     unused << handler.forget(); // picked up by callback handler
  1588     return true;
  1590 };
  1592 class AddReservedServiceRecordsTask : public Task
  1594 public:
  1595   AddReservedServiceRecordsTask()
  1596   { }
  1598   void Run()
  1600     static const dbus_uint32_t sServices[] = {
  1601       BluetoothServiceClass::HANDSFREE_AG,
  1602       BluetoothServiceClass::HEADSET_AG,
  1603       BluetoothServiceClass::OBJECT_PUSH
  1604     };
  1606     MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
  1607     MOZ_ASSERT(sDBusConnection);
  1608     MOZ_ASSERT(!sAdapterPath.IsEmpty());
  1610     nsRefPtr<DBusReplyHandler> handler =
  1611       new AddReservedServiceRecordsReplyHandler();
  1613     const dbus_uint32_t* services = sServices;
  1615     bool success = sDBusConnection->SendWithReply(
  1616       DBusReplyHandler::Callback, handler.get(), -1,
  1617       BLUEZ_DBUS_BASE_IFC,
  1618       NS_ConvertUTF16toUTF8(sAdapterPath).get(),
  1619       DBUS_ADAPTER_IFACE, "AddReservedServiceRecords",
  1620       DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32,
  1621       &services, ArrayLength(sServices), DBUS_TYPE_INVALID);
  1623     NS_ENSURE_TRUE_VOID(success);
  1625     unused << handler.forget(); /* picked up by callback handler */
  1627 };
  1629 class PrepareAdapterRunnable : public nsRunnable
  1631 public:
  1632   PrepareAdapterRunnable()
  1633   { }
  1635   NS_IMETHOD Run()
  1637     MOZ_ASSERT(NS_IsMainThread());
  1639     Task* task = new AddReservedServiceRecordsTask();
  1640     DispatchToDBusThread(task);
  1642     return NS_OK;
  1644 };
  1646 class RequestPlayStatusTask : public nsRunnable
  1648 public:
  1649   RequestPlayStatusTask()
  1651     MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
  1654   nsresult Run()
  1656     MOZ_ASSERT(NS_IsMainThread());
  1658     BluetoothSignal signal(NS_LITERAL_STRING(REQUEST_MEDIA_PLAYSTATUS_ID),
  1659                            NS_LITERAL_STRING(KEY_ADAPTER),
  1660                            InfallibleTArray<BluetoothNamedValue>());
  1662     BluetoothService* bs = BluetoothService::Get();
  1663     NS_ENSURE_TRUE(bs, NS_ERROR_FAILURE);
  1664     bs->DistributeSignal(signal);
  1666     return NS_OK;
  1668 };
  1670 // Called by dbus during WaitForAndDispatchEventNative()
  1671 // This function is called on the IOThread
  1672 static DBusHandlerResult
  1673 EventFilter(DBusConnection* aConn, DBusMessage* aMsg, void* aData)
  1675   // I/O thread
  1676   MOZ_ASSERT(!NS_IsMainThread(), "Shouldn't be called from Main Thread!");
  1678   if (dbus_message_get_type(aMsg) != DBUS_MESSAGE_TYPE_SIGNAL) {
  1679     BT_WARNING("%s: event handler not interested in %s (not a signal).\n",
  1680         __FUNCTION__, dbus_message_get_member(aMsg));
  1681     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
  1684   if (dbus_message_get_path(aMsg) == nullptr) {
  1685     BT_WARNING("DBusMessage %s has no bluetooth destination, ignoring\n",
  1686                dbus_message_get_member(aMsg));
  1687     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
  1690   DBusError err;
  1691   dbus_error_init(&err);
  1693   nsAutoString signalPath;
  1694   nsAutoString signalName;
  1695   nsAutoString signalInterface;
  1697   BT_LOGD("%s: %s, %s, %s", __FUNCTION__,
  1698                           dbus_message_get_interface(aMsg),
  1699                           dbus_message_get_path(aMsg),
  1700                           dbus_message_get_member(aMsg));
  1702   signalInterface = NS_ConvertUTF8toUTF16(dbus_message_get_interface(aMsg));
  1703   signalPath = NS_ConvertUTF8toUTF16(dbus_message_get_path(aMsg));
  1704   signalName = NS_ConvertUTF8toUTF16(dbus_message_get_member(aMsg));
  1705   nsString errorStr;
  1706   BluetoothValue v;
  1708   // Since the signalPath extracted from dbus message is a object path,
  1709   // we'd like to re-assign them to corresponding key entry in
  1710   // BluetoothSignalObserverTable
  1711   if (signalInterface.EqualsLiteral(DBUS_MANAGER_IFACE)) {
  1712     signalPath.AssignLiteral(KEY_MANAGER);
  1713   } else if (signalInterface.EqualsLiteral(DBUS_ADAPTER_IFACE)) {
  1714     signalPath.AssignLiteral(KEY_ADAPTER);
  1715   } else if (signalInterface.EqualsLiteral(DBUS_DEVICE_IFACE)){
  1716     signalPath = GetAddressFromObjectPath(signalPath);
  1719   if (dbus_message_is_signal(aMsg, DBUS_ADAPTER_IFACE, "DeviceFound")) {
  1720     DBusMessageIter iter;
  1722     if (!dbus_message_iter_init(aMsg, &iter)) {
  1723       BT_WARNING("Can't create iterator!");
  1724       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
  1727     const char* addr;
  1728     dbus_message_iter_get_basic(&iter, &addr);
  1730     if (!dbus_message_iter_next(&iter)) {
  1731       errorStr.AssignLiteral("Unexpected message struct in msg DeviceFound");
  1732     } else {
  1733       ParseProperties(&iter,
  1734                       v,
  1735                       errorStr,
  1736                       sDeviceProperties,
  1737                       ArrayLength(sDeviceProperties));
  1739       InfallibleTArray<BluetoothNamedValue>& properties =
  1740         v.get_ArrayOfBluetoothNamedValue();
  1742       // The DBus DeviceFound message actually passes back a key value object
  1743       // with the address as the key and the rest of the device properties as
  1744       // a dict value. After we parse out the properties, we need to go back
  1745       // and add the address to the ipdl dict we've created to make sure we
  1746       // have all of the information to correctly build the device.
  1747       nsAutoString address = NS_ConvertUTF8toUTF16(addr);
  1748       properties.AppendElement(
  1749         BluetoothNamedValue(NS_LITERAL_STRING("Address"), address));
  1750       properties.AppendElement(
  1751         BluetoothNamedValue(NS_LITERAL_STRING("Path"),
  1752                             GetObjectPathFromAddress(signalPath, address)));
  1754       if (!ContainsIcon(properties)) {
  1755         for (uint32_t i = 0; i < properties.Length(); i++) {
  1756           // It is possible that property Icon missed due to CoD of major
  1757           // class is TOY but service class is "Audio", we need to assign
  1758           // Icon as audio-card. This is for PTS test TC_AG_COD_BV_02_I.
  1759           // As HFP specification defined that
  1760           // service class is "Audio" can be considered as HFP AG.
  1761           if (properties[i].name().EqualsLiteral("Class")) {
  1762             if (HasAudioService(properties[i].value().get_uint32_t())) {
  1763               v.get_ArrayOfBluetoothNamedValue().AppendElement(
  1764                 BluetoothNamedValue(NS_LITERAL_STRING("Icon"),
  1765                                     NS_LITERAL_STRING("audio-card")));
  1767             break;
  1772   } else if (dbus_message_is_signal(aMsg, DBUS_ADAPTER_IFACE,
  1773                                     "DeviceDisappeared")) {
  1774     const char* str;
  1775     if (!dbus_message_get_args(aMsg, &err,
  1776                                DBUS_TYPE_STRING, &str,
  1777                                DBUS_TYPE_INVALID)) {
  1778       LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, aMsg);
  1779       errorStr.AssignLiteral("Cannot parse device address!");
  1780     } else {
  1781       v = NS_ConvertUTF8toUTF16(str);
  1783   } else if (dbus_message_is_signal(aMsg, DBUS_ADAPTER_IFACE,
  1784                                     "DeviceCreated")) {
  1785     const char* str;
  1786     if (!dbus_message_get_args(aMsg, &err,
  1787                                DBUS_TYPE_OBJECT_PATH, &str,
  1788                                DBUS_TYPE_INVALID)) {
  1789       LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, aMsg);
  1790       errorStr.AssignLiteral("Cannot parse device path!");
  1791     } else {
  1792       v = NS_ConvertUTF8toUTF16(str);
  1794   } else if (dbus_message_is_signal(aMsg, DBUS_ADAPTER_IFACE,
  1795                                     "DeviceRemoved")) {
  1796     const char* str;
  1797     if (!dbus_message_get_args(aMsg, &err,
  1798                                DBUS_TYPE_OBJECT_PATH, &str,
  1799                                DBUS_TYPE_INVALID)) {
  1800       LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, aMsg);
  1801       errorStr.AssignLiteral("Cannot parse device path!");
  1802     } else {
  1803       v = NS_ConvertUTF8toUTF16(str);
  1805   } else if (dbus_message_is_signal(aMsg, DBUS_ADAPTER_IFACE,
  1806                                     "PropertyChanged")) {
  1807     ParsePropertyChange(aMsg,
  1808                         v,
  1809                         errorStr,
  1810                         sAdapterProperties,
  1811                         ArrayLength(sAdapterProperties));
  1812   } else if (dbus_message_is_signal(aMsg, DBUS_DEVICE_IFACE,
  1813                                     "PropertyChanged")) {
  1814     ParsePropertyChange(aMsg,
  1815                         v,
  1816                         errorStr,
  1817                         sDeviceProperties,
  1818                         ArrayLength(sDeviceProperties));
  1820     if (v.type() == BluetoothValue::T__None) {
  1821       BT_WARNING("PropertyChanged event couldn't be parsed.");
  1822       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
  1825     BluetoothNamedValue& property = v.get_ArrayOfBluetoothNamedValue()[0];
  1826     if (property.name().EqualsLiteral("Paired")) {
  1827       // Original approach: Broadcast system message of
  1828       // "bluetooth-pairedstatuschanged" from BluetoothService.
  1829       BluetoothValue newValue(v);
  1830       ToLowerCase(newValue.get_ArrayOfBluetoothNamedValue()[0].name());
  1831       BluetoothSignal signal(NS_LITERAL_STRING(PAIRED_STATUS_CHANGED_ID),
  1832                              NS_LITERAL_STRING(KEY_LOCAL_AGENT),
  1833                              newValue);
  1834       NS_DispatchToMainThread(new DistributeBluetoothSignalTask(signal));
  1836       // New approach: Dispatch event from BluetoothAdapter
  1837       bool status = property.value();
  1838       InfallibleTArray<BluetoothNamedValue> parameters;
  1839       parameters.AppendElement(
  1840         BluetoothNamedValue(NS_LITERAL_STRING("address"), signalPath));
  1841       parameters.AppendElement(
  1842         BluetoothNamedValue(NS_LITERAL_STRING("status"), status));
  1843       signal.path() = NS_LITERAL_STRING(KEY_ADAPTER);
  1844       signal.value() = parameters;
  1845       NS_DispatchToMainThread(new DistributeBluetoothSignalTask(signal));
  1846     } else if (property.name().EqualsLiteral("Connected")) {
  1847       MonitorAutoLock lock(*sStopBluetoothMonitor);
  1849       if (property.value().get_bool()) {
  1850         ++sConnectedDeviceCount;
  1851       } else {
  1852         MOZ_ASSERT(sConnectedDeviceCount > 0);
  1853         if (--sConnectedDeviceCount == 0) {
  1854           lock.Notify();
  1858   } else if (dbus_message_is_signal(aMsg, DBUS_MANAGER_IFACE, "AdapterAdded")) {
  1859     const char* str;
  1860     if (!dbus_message_get_args(aMsg, &err,
  1861                                DBUS_TYPE_OBJECT_PATH, &str,
  1862                                DBUS_TYPE_INVALID)) {
  1863       LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, aMsg);
  1864       errorStr.AssignLiteral("Cannot parse manager path!");
  1865     } else {
  1866       v = NS_ConvertUTF8toUTF16(str);
  1867       sAdapterPath = v.get_nsString();
  1868       NS_DispatchToMainThread(new TryFiringAdapterAddedRunnable(true));
  1869       NS_DispatchToMainThread(new PrepareAdapterRunnable());
  1871       /**
  1872        * The adapter name isn't ready for the time being. Wait for the upcoming
  1873        * signal PropertyChanged of adapter name, and then propagate signal
  1874        * AdapterAdded to BluetoothManager.
  1875        */
  1876       return DBUS_HANDLER_RESULT_HANDLED;
  1878   } else if (dbus_message_is_signal(aMsg, DBUS_MANAGER_IFACE,
  1879                                     "PropertyChanged")) {
  1880     ParsePropertyChange(aMsg,
  1881                         v,
  1882                         errorStr,
  1883                         sManagerProperties,
  1884                         ArrayLength(sManagerProperties));
  1885   } else if (dbus_message_is_signal(aMsg, DBUS_SINK_IFACE,
  1886                                     "PropertyChanged")) {
  1887     ParsePropertyChange(aMsg,
  1888                         v,
  1889                         errorStr,
  1890                         sSinkProperties,
  1891                         ArrayLength(sSinkProperties));
  1892   } else if (dbus_message_is_signal(aMsg, DBUS_CTL_IFACE, "GetPlayStatus")) {
  1893     NS_DispatchToMainThread(new RequestPlayStatusTask());
  1894     return DBUS_HANDLER_RESULT_HANDLED;
  1895   } else if (dbus_message_is_signal(aMsg, DBUS_CTL_IFACE, "PropertyChanged")) {
  1896     ParsePropertyChange(aMsg,
  1897                         v,
  1898                         errorStr,
  1899                         sControlProperties,
  1900                         ArrayLength(sControlProperties));
  1901   } else if (dbus_message_is_signal(aMsg, DBUS_INPUT_IFACE,
  1902                                     "PropertyChanged")) {
  1903     ParsePropertyChange(aMsg,
  1904                         v,
  1905                         errorStr,
  1906                         sInputProperties,
  1907                         ArrayLength(sInputProperties));
  1908   } else {
  1909     errorStr = NS_ConvertUTF8toUTF16(dbus_message_get_member(aMsg));
  1910     errorStr.AppendLiteral(" Signal not handled!");
  1913   if (!errorStr.IsEmpty()) {
  1914     BT_WARNING(NS_ConvertUTF16toUTF8(errorStr).get());
  1915     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
  1918   BluetoothSignal signal(signalName, signalPath, v);
  1919   nsRefPtr<nsRunnable> task;
  1920   if (signalInterface.EqualsLiteral(DBUS_SINK_IFACE)) {
  1921     task = new SinkPropertyChangedHandler(signal);
  1922   } else if (signalInterface.EqualsLiteral(DBUS_CTL_IFACE)) {
  1923     task = new ControlPropertyChangedHandler(signal);
  1924   } else if (signalInterface.EqualsLiteral(DBUS_INPUT_IFACE)) {
  1925     task = new InputPropertyChangedHandler(signal);
  1926   } else {
  1927     task = new DistributeBluetoothSignalTask(signal);
  1930   NS_DispatchToMainThread(task);
  1932   return DBUS_HANDLER_RESULT_HANDLED;
  1935 static void
  1936 OnDefaultAdapterReply(DBusMessage* aReply, void* aData)
  1938   MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
  1940   if (!aReply || dbus_message_is_error(aReply, DBUS_ERROR_TIMEOUT)) {
  1941     return;
  1944   DBusError err;
  1945   dbus_error_init(&err);
  1947   BluetoothValue v;
  1948   nsAutoString errorString;
  1950   UnpackObjectPathMessage(aReply, &err, v, errorString);
  1952   if (!errorString.IsEmpty()) {
  1953     return;
  1956   sAdapterPath = v.get_nsString();
  1958   nsRefPtr<PrepareAdapterRunnable> b = new PrepareAdapterRunnable();
  1959   if (NS_FAILED(NS_DispatchToMainThread(b))) {
  1960     BT_WARNING("Failed to dispatch to main thread!");
  1964 bool
  1965 BluetoothDBusService::IsReady()
  1967   if (!IsEnabled() || !sDBusConnection || IsToggling()) {
  1968     BT_WARNING("Bluetooth service is not ready yet!");
  1969     return false;
  1971   return true;
  1974 class StartDBusConnectionTask : public Task
  1976 public:
  1977   StartDBusConnectionTask(RawDBusConnection* aConnection)
  1978   : mConnection(aConnection)
  1980     MOZ_ASSERT(mConnection);
  1983   void Run()
  1985     MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
  1987     if (sDBusConnection) {
  1988       BT_WARNING("DBus connection has already been established.");
  1989       nsRefPtr<nsRunnable> runnable = new BluetoothService::ToggleBtAck(true);
  1990       if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
  1991         BT_WARNING("Failed to dispatch to main thread!");
  1993       return;
  1996     // Add a filter for all incoming messages_base
  1997     if (!dbus_connection_add_filter(mConnection->GetConnection(),
  1998                                     EventFilter, nullptr, nullptr)) {
  1999       BT_WARNING("Cannot create DBus Event Filter for DBus Thread!");
  2000       nsRefPtr<nsRunnable> runnable = new BluetoothService::ToggleBtAck(false);
  2001       if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
  2002         BT_WARNING("Failed to dispatch to main thread!");
  2004       return;
  2007     mConnection->Watch();
  2009     if (!sPairingReqTable) {
  2010       sPairingReqTable = new nsDataHashtable<nsStringHashKey, DBusMessage* >;
  2013     sDBusConnection = mConnection.forget();
  2015     nsRefPtr<nsRunnable> runnable =
  2016       new BluetoothService::ToggleBtAck(true);
  2017     if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
  2018       BT_WARNING("Failed to dispatch to main thread!");
  2019       return;
  2022     /* Normally we'll receive the signal 'AdapterAdded' with the adapter object
  2023      * path from the DBus daemon during start up. So, there's no need to query
  2024      * the object path of default adapter here. However, if we restart from a
  2025      * crash, the default adapter might already be available, so we ask the daemon
  2026      * explicitly here.
  2027      */
  2028     if (sAdapterPath.IsEmpty()) {
  2029       bool success = sDBusConnection->SendWithReply(OnDefaultAdapterReply, nullptr,
  2030                                                     1000, BLUEZ_DBUS_BASE_IFC, "/",
  2031                                                     DBUS_MANAGER_IFACE,
  2032                                                     "DefaultAdapter",
  2033                                                     DBUS_TYPE_INVALID);
  2034       if (!success) {
  2035         BT_WARNING("Failed to query default adapter!");
  2040 private:
  2041   nsAutoPtr<RawDBusConnection> mConnection;
  2042 };
  2044 class StartBluetoothRunnable MOZ_FINAL : public nsRunnable
  2046 public:
  2047   NS_IMETHOD Run()
  2049     // This could block. It should never be run on the main thread.
  2050     MOZ_ASSERT(!NS_IsMainThread()); // BT thread
  2052 #ifdef MOZ_WIDGET_GONK
  2053     if (!sBluedroid.Enable()) {
  2054       BT_WARNING("Bluetooth not available.");
  2055       nsRefPtr<nsRunnable> runnable = new BluetoothService::ToggleBtAck(false);
  2056       if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
  2057         BT_WARNING("Failed to dispatch to main thread!");
  2059       return NS_ERROR_FAILURE;
  2061 #endif
  2063     RawDBusConnection* connection = new RawDBusConnection();
  2064     nsresult rv = connection->EstablishDBusConnection();
  2065     if (NS_FAILED(rv)) {
  2066       BT_WARNING("Failed to establish connection to BlueZ daemon");
  2067       nsRefPtr<nsRunnable> runnable = new BluetoothService::ToggleBtAck(false);
  2068       if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
  2069         BT_WARNING("Failed to dispatch to main thread!");
  2071       return NS_ERROR_FAILURE;
  2074     DBusError err;
  2075     dbus_error_init(&err);
  2077     // Set which messages will be processed by this dbus connection.
  2078     // Since we are maintaining a single thread for all the DBus bluez
  2079     // signals we want, register all of them in this thread at startup.
  2080     // The event handler will sort the destinations out as needed. The
  2081     // call to dbus_bus_add_match has to run on the BT thread because
  2082     // it can block.
  2083     for (uint32_t i = 0; i < ArrayLength(sBluetoothDBusSignals); ++i) {
  2084       dbus_bus_add_match(connection->GetConnection(),
  2085                          sBluetoothDBusSignals[i],
  2086                          &err);
  2087       if (dbus_error_is_set(&err)) {
  2088         LOG_AND_FREE_DBUS_ERROR(&err);
  2092     Task* task = new StartDBusConnectionTask(connection);
  2093     DispatchToDBusThread(task);
  2095     return NS_OK;
  2097 };
  2099 nsresult
  2100 BluetoothDBusService::StartInternal()
  2102   nsRefPtr<nsRunnable> runnable = new StartBluetoothRunnable();
  2103   nsresult rv = DispatchToBtThread(runnable);
  2104   if (NS_FAILED(rv)) {
  2105     BT_WARNING("Failed to dispatch to BT thread!");
  2107   return rv;
  2110 class DisableBluetoothRunnable MOZ_FINAL : public nsRunnable
  2112 public:
  2113   NS_IMETHOD Run()
  2115     if (NS_IsMainThread()) {
  2116       // Clear |sControllerArray| here while we're on the main thread
  2117       sControllerArray.Clear();
  2118       // Forward this runnable to BT thread
  2119       return DispatchToBtThread(this);
  2122 #ifdef MOZ_WIDGET_GONK
  2123     MOZ_ASSERT(sBluedroid.IsEnabled());
  2124     // Disable() return true on success, so we need to invert it
  2125     bool isEnabled = !sBluedroid.Disable();
  2126 #else
  2127     bool isEnabled = false;
  2128 #endif
  2130     nsRefPtr<nsRunnable> runnable =
  2131       new BluetoothService::ToggleBtAck(isEnabled);
  2132     nsresult rv = NS_DispatchToMainThread(runnable);
  2133     if (NS_FAILED(rv)) {
  2134       BT_WARNING("Failed to dispatch to main thread!");
  2136     return rv;
  2138 };
  2140 class DeleteDBusConnectionTask MOZ_FINAL : public Task
  2142 public:
  2143   DeleteDBusConnectionTask()
  2144   { }
  2146   void Run()
  2148     MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
  2150     if (!sDBusConnection) {
  2151       BT_WARNING("DBus connection has not been established.");
  2152       nsRefPtr<nsRunnable> runnable = new BluetoothService::ToggleBtAck(false);
  2153       if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
  2154         BT_WARNING("Failed to dispatch to main thread!");
  2156       return;
  2159     for (uint32_t i = 0; i < ArrayLength(sBluetoothDBusSignals); ++i) {
  2160       dbus_bus_remove_match(sDBusConnection->GetConnection(),
  2161                             sBluetoothDBusSignals[i], NULL);
  2164     dbus_connection_remove_filter(sDBusConnection->GetConnection(),
  2165                                   EventFilter, nullptr);
  2167     if (!dbus_connection_unregister_object_path(sDBusConnection->GetConnection(),
  2168                                                 KEY_LOCAL_AGENT)) {
  2169       BT_WARNING("%s: Can't unregister object path %s for agent!",
  2170           __FUNCTION__, KEY_LOCAL_AGENT);
  2173     if (!dbus_connection_unregister_object_path(sDBusConnection->GetConnection(),
  2174                                                 KEY_REMOTE_AGENT)) {
  2175       BT_WARNING("%s: Can't unregister object path %s for agent!",
  2176           __FUNCTION__, KEY_REMOTE_AGENT);
  2179     // unref stored DBusMessages before clearing the hashtable
  2180     sPairingReqTable->EnumerateRead(UnrefDBusMessage, nullptr);
  2181     sPairingReqTable->Clear();
  2183     sIsPairing = 0;
  2184     sConnectedDeviceCount = 0;
  2186     // This command closes the DBus connection and all its instances
  2187     // of DBusWatch will be removed and free'd.
  2188     sDBusConnection = nullptr;
  2190     // We can only dispatch to the BT thread if we're on the main
  2191     // thread. Thus we dispatch our runnable to the main thread
  2192     // from where it will forward itself to the BT thread.
  2193     nsRefPtr<nsRunnable> runnable = new DisableBluetoothRunnable();
  2194     if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
  2195       BT_WARNING("Failed to dispatch to BT thread!");
  2199 private:
  2200   static PLDHashOperator
  2201   UnrefDBusMessage(const nsAString& key, DBusMessage* value, void* arg)
  2203     dbus_message_unref(value);
  2204     return PL_DHASH_NEXT;
  2206 };
  2208 class StopBluetoothRunnable MOZ_FINAL : public nsRunnable
  2210 public:
  2211   NS_IMETHOD Run()
  2213     MOZ_ASSERT(!NS_IsMainThread()); // BT thread
  2215     // This could block. It should never be run on the main thread.
  2216     MonitorAutoLock lock(*sStopBluetoothMonitor);
  2217     if (sConnectedDeviceCount > 0) {
  2218       lock.Wait(PR_SecondsToInterval(TIMEOUT_FORCE_TO_DISABLE_BT));
  2221     DispatchToDBusThread(new DeleteDBusConnectionTask());
  2223     return NS_OK;
  2225 };
  2227 nsresult
  2228 BluetoothDBusService::StopInternal()
  2230   nsRefPtr<nsRunnable> runnable = new StopBluetoothRunnable();
  2231   nsresult rv = DispatchToBtThread(runnable);
  2232   if (NS_FAILED(rv)) {
  2233     BT_WARNING("Failed to dispatch to BT thread!");
  2235   return rv;
  2238 class DefaultAdapterPathReplyHandler : public DBusReplyHandler
  2240 public:
  2241   DefaultAdapterPathReplyHandler(BluetoothReplyRunnable* aRunnable)
  2242     : mRunnable(aRunnable)
  2244     MOZ_ASSERT(mRunnable);
  2247   void Handle(DBusMessage* aReply) MOZ_OVERRIDE
  2249     MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
  2251     if (!aReply || (dbus_message_get_type(aReply) == DBUS_MESSAGE_TYPE_ERROR)) {
  2252       const char* errStr = "Timeout in DefaultAdapterPathReplyHandler";
  2253       if (aReply) {
  2254         errStr = dbus_message_get_error_name(aReply);
  2255         if (!errStr) {
  2256           errStr = "Bluetooth DBus Error";
  2259       DispatchBluetoothReply(mRunnable, BluetoothValue(),
  2260                              NS_ConvertUTF8toUTF16(errStr));
  2261       return;
  2264     bool success;
  2265     nsAutoString replyError;
  2267     if (mAdapterPath.IsEmpty()) {
  2268       success = HandleDefaultAdapterPathReply(aReply, replyError);
  2269     } else {
  2270       success = HandleGetPropertiesReply(aReply, replyError);
  2273     if (!success) {
  2274       DispatchBluetoothReply(mRunnable, BluetoothValue(), replyError);
  2278 protected:
  2279   bool HandleDefaultAdapterPathReply(DBusMessage* aReply,
  2280                                      nsAString& aReplyError)
  2282     BluetoothValue value;
  2283     DBusError error;
  2284     dbus_error_init(&error);
  2286     MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
  2287     MOZ_ASSERT(sDBusConnection);
  2289     UnpackObjectPathMessage(aReply, &error, value, aReplyError);
  2291     if (!aReplyError.IsEmpty()) {
  2292       return false;
  2295     mAdapterPath = value.get_nsString();
  2297     // Acquire another reference to this reply handler
  2298     nsRefPtr<DefaultAdapterPathReplyHandler> handler = this;
  2300     bool success = sDBusConnection->SendWithReply(
  2301       DefaultAdapterPathReplyHandler::Callback, handler.get(), 1000,
  2302       BLUEZ_DBUS_BASE_IFC,
  2303       NS_ConvertUTF16toUTF8(mAdapterPath).get(),
  2304       DBUS_ADAPTER_IFACE, "GetProperties", DBUS_TYPE_INVALID);
  2306     if (!success) {
  2307       aReplyError = NS_LITERAL_STRING("SendWithReply failed");
  2308       return false;
  2311     unused << handler.forget(); // picked up by callback handler
  2313     return true;
  2316   bool HandleGetPropertiesReply(DBusMessage* aReply,
  2317                                 nsAutoString& aReplyError)
  2319     BluetoothValue value;
  2320     DBusError error;
  2321     dbus_error_init(&error);
  2323     MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
  2325     bool success = UnpackPropertiesMessage(aReply, &error, value,
  2326                                            DBUS_ADAPTER_IFACE);
  2327     if (!success) {
  2328       aReplyError = NS_ConvertUTF8toUTF16(error.message);
  2329       return false;
  2332     // We have to manually attach the path to the rest of the elements
  2333     value.get_ArrayOfBluetoothNamedValue().AppendElement(
  2334       BluetoothNamedValue(NS_LITERAL_STRING("Path"), mAdapterPath));
  2336     // Dispatch result
  2337     DispatchBluetoothReply(mRunnable, value, aReplyError);
  2339     return true;
  2342 private:
  2343   nsRefPtr<BluetoothReplyRunnable> mRunnable;
  2344   nsString mAdapterPath;
  2345 };
  2347 class DefaultAdapterTask : public Task
  2349 public:
  2350   DefaultAdapterTask(BluetoothReplyRunnable* aRunnable)
  2351     : mRunnable(aRunnable)
  2353     MOZ_ASSERT(mRunnable);
  2356   void Run() MOZ_OVERRIDE
  2358     MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
  2359     MOZ_ASSERT(sDBusConnection);
  2361     nsRefPtr<DefaultAdapterPathReplyHandler> handler =
  2362       new DefaultAdapterPathReplyHandler(mRunnable);
  2364     bool success = sDBusConnection->SendWithReply(
  2365       DefaultAdapterPathReplyHandler::Callback,
  2366       handler.get(), 1000, BLUEZ_DBUS_BASE_IFC,
  2367       "/", DBUS_MANAGER_IFACE, "DefaultAdapter",
  2368       DBUS_TYPE_INVALID);
  2369     NS_ENSURE_TRUE_VOID(success);
  2371     unused << handler.forget(); // picked up by callback handler
  2374 private:
  2375   nsRefPtr<BluetoothReplyRunnable> mRunnable;
  2376 };
  2378 nsresult
  2379 BluetoothDBusService::GetDefaultAdapterPathInternal(
  2380                                               BluetoothReplyRunnable* aRunnable)
  2382   MOZ_ASSERT(NS_IsMainThread());
  2384   if (!IsReady()) {
  2385     NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!");
  2386     DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr);
  2387     return NS_OK;
  2390   Task* task = new DefaultAdapterTask(aRunnable);
  2391   DispatchToDBusThread(task);
  2393   return NS_OK;
  2396 static void
  2397 OnSendDiscoveryMessageReply(DBusMessage *aReply, void *aData)
  2399   MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
  2401   nsAutoString errorStr;
  2403   if (!aReply) {
  2404     errorStr.AssignLiteral("SendDiscovery failed");
  2407   nsRefPtr<BluetoothReplyRunnable> runnable =
  2408     dont_AddRef<BluetoothReplyRunnable>(static_cast<BluetoothReplyRunnable*>(aData));
  2410   DispatchBluetoothReply(runnable.get(), BluetoothValue(true), errorStr);
  2413 class SendDiscoveryMessageTask : public Task
  2415 public:
  2416   SendDiscoveryMessageTask(const char* aMessageName,
  2417                            BluetoothReplyRunnable* aRunnable)
  2418     : mMessageName(aMessageName)
  2419     , mRunnable(aRunnable)
  2421     MOZ_ASSERT(!mMessageName.IsEmpty());
  2422     MOZ_ASSERT(mRunnable);
  2425   void Run() MOZ_OVERRIDE
  2427     MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
  2428     MOZ_ASSERT(sDBusConnection);
  2429     MOZ_ASSERT(!sAdapterPath.IsEmpty());
  2431     bool success = sDBusConnection->SendWithReply(
  2432       OnSendDiscoveryMessageReply,
  2433       static_cast<void*>(mRunnable.get()), -1,
  2434       BLUEZ_DBUS_BASE_IFC,
  2435       NS_ConvertUTF16toUTF8(sAdapterPath).get(),
  2436       DBUS_ADAPTER_IFACE, mMessageName.get(),
  2437       DBUS_TYPE_INVALID);
  2438     NS_ENSURE_TRUE_VOID(success);
  2440     unused << mRunnable.forget(); // picked up by callback handler
  2443 private:
  2444   const nsCString mMessageName;
  2445   nsRefPtr<BluetoothReplyRunnable> mRunnable;
  2446 };
  2448 nsresult
  2449 BluetoothDBusService::SendDiscoveryMessage(const char* aMessageName,
  2450                                            BluetoothReplyRunnable* aRunnable)
  2452   MOZ_ASSERT(NS_IsMainThread());
  2453   MOZ_ASSERT(!sAdapterPath.IsEmpty());
  2455   if (!IsReady()) {
  2456     NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!");
  2457     DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr);
  2458     return NS_OK;
  2461   Task* task = new SendDiscoveryMessageTask(aMessageName, aRunnable);
  2462   DispatchToDBusThread(task);
  2464   return NS_OK;
  2467 nsresult
  2468 BluetoothDBusService::SendInputMessage(const nsAString& aDeviceAddress,
  2469                                        const nsAString& aMessage)
  2471   DBusReplyCallback callback;
  2472   if (aMessage.EqualsLiteral("Connect")) {
  2473     callback = InputConnectCallback;
  2474   } else if (aMessage.EqualsLiteral("Disconnect")) {
  2475     callback = InputDisconnectCallback;
  2476   } else {
  2477     MOZ_ASSERT(false);
  2478     return NS_ERROR_FAILURE;
  2481   MOZ_ASSERT(!sAdapterPath.IsEmpty());
  2482   nsString objectPath = GetObjectPathFromAddress(sAdapterPath, aDeviceAddress);
  2483   return SendAsyncDBusMessage(objectPath, DBUS_INPUT_IFACE, aMessage, callback);
  2486 class SendAsyncDBusMessageTask : public Task
  2488 public:
  2489   SendAsyncDBusMessageTask(DBusReplyCallback aCallback,
  2490                            BluetoothServiceClass* aServiceClass,
  2491                            const nsACString& aObjectPath,
  2492                            const char* aInterface,
  2493                            const nsACString& aMessage)
  2494     : mCallback(aCallback)
  2495     , mServiceClass(aServiceClass)
  2496     , mObjectPath(aObjectPath)
  2497     , mInterface(aInterface)
  2498     , mMessage(aMessage)
  2500     MOZ_ASSERT(mServiceClass);
  2501     MOZ_ASSERT(!mObjectPath.IsEmpty());
  2502     MOZ_ASSERT(!mInterface.IsEmpty());
  2503     MOZ_ASSERT(!mMessage.IsEmpty());
  2506   void Run() MOZ_OVERRIDE
  2508     MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
  2509     MOZ_ASSERT(sDBusConnection);
  2511     bool success = sDBusConnection->SendWithReply(
  2512       mCallback, static_cast<void*>(mServiceClass), -1,
  2513       BLUEZ_DBUS_BASE_IFC, mObjectPath.get(), mInterface.get(),
  2514       mMessage.get(), DBUS_TYPE_INVALID);
  2515     NS_ENSURE_TRUE_VOID(success);
  2517     mServiceClass.forget();
  2520 private:
  2521   DBusReplyCallback mCallback;
  2522   nsAutoPtr<BluetoothServiceClass> mServiceClass;
  2523   const nsCString mObjectPath;
  2524   const nsCString mInterface;
  2525   const nsCString mMessage;
  2526 };
  2528 nsresult
  2529 BluetoothDBusService::SendAsyncDBusMessage(const nsAString& aObjectPath,
  2530                                            const char* aInterface,
  2531                                            const nsAString& aMessage,
  2532                                            DBusReplyCallback aCallback)
  2534   MOZ_ASSERT(NS_IsMainThread());
  2535   MOZ_ASSERT(IsEnabled());
  2536   MOZ_ASSERT(aCallback);
  2537   MOZ_ASSERT(!aObjectPath.IsEmpty());
  2538   MOZ_ASSERT(aInterface);
  2540   nsAutoPtr<BluetoothServiceClass> serviceClass(new BluetoothServiceClass());
  2541   if (!strcmp(aInterface, DBUS_SINK_IFACE)) {
  2542     *serviceClass = BluetoothServiceClass::A2DP;
  2543   } else if (!strcmp(aInterface, DBUS_INPUT_IFACE)) {
  2544     *serviceClass = BluetoothServiceClass::HID;
  2545   } else {
  2546     MOZ_ASSERT(false);
  2547     return NS_ERROR_FAILURE;
  2550   Task* task = new SendAsyncDBusMessageTask(aCallback,
  2551                                             serviceClass.forget(),
  2552                                             NS_ConvertUTF16toUTF8(aObjectPath),
  2553                                             aInterface,
  2554                                             NS_ConvertUTF16toUTF8(aMessage));
  2555   DispatchToDBusThread(task);
  2557   return NS_OK;
  2560 nsresult
  2561 BluetoothDBusService::SendSinkMessage(const nsAString& aDeviceAddress,
  2562                                       const nsAString& aMessage)
  2564   DBusReplyCallback callback;
  2565   if (aMessage.EqualsLiteral("Connect")) {
  2566     callback = SinkConnectCallback;
  2567   } else if (aMessage.EqualsLiteral("Disconnect")) {
  2568     callback = SinkDisconnectCallback;
  2569   } else {
  2570     MOZ_ASSERT(false);
  2571     return NS_ERROR_FAILURE;
  2574   MOZ_ASSERT(!sAdapterPath.IsEmpty());
  2575   nsString objectPath = GetObjectPathFromAddress(sAdapterPath, aDeviceAddress);
  2576   return SendAsyncDBusMessage(objectPath, DBUS_SINK_IFACE, aMessage, callback);
  2579 nsresult
  2580 BluetoothDBusService::StopDiscoveryInternal(BluetoothReplyRunnable* aRunnable)
  2582   return SendDiscoveryMessage("StopDiscovery", aRunnable);
  2585 nsresult
  2586 BluetoothDBusService::StartDiscoveryInternal(BluetoothReplyRunnable* aRunnable)
  2588   return SendDiscoveryMessage("StartDiscovery", aRunnable);
  2591 class BluetoothArrayOfDevicePropertiesReplyHandler : public DBusReplyHandler
  2593 public:
  2594   BluetoothArrayOfDevicePropertiesReplyHandler(
  2595     const nsTArray<nsString>& aDeviceAddresses,
  2596     const FilterFunc aFilterFunc, BluetoothReplyRunnable* aRunnable)
  2597     : mDeviceAddresses(aDeviceAddresses)
  2598     , mProcessedDeviceAddresses(0)
  2599     , mFilterFunc(aFilterFunc)
  2600     , mRunnable(aRunnable)
  2601     , mValues(InfallibleTArray<BluetoothNamedValue>())
  2603     MOZ_ASSERT(mRunnable);
  2606   void Handle(DBusMessage* aReply) MOZ_OVERRIDE
  2608     MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
  2609     MOZ_ASSERT(!sAdapterPath.IsEmpty());
  2610     MOZ_ASSERT(!mObjectPath.IsEmpty());
  2611     MOZ_ASSERT(mProcessedDeviceAddresses < mDeviceAddresses.Length());
  2613     const nsTArray<nsString>::index_type i = mProcessedDeviceAddresses++;
  2615     if (!aReply ||
  2616         (dbus_message_get_type(aReply) == DBUS_MESSAGE_TYPE_ERROR)) {
  2617       BT_WARNING("Invalid DBus message");
  2618       ProcessRemainingDeviceAddresses();
  2619       return;
  2622     // Get device properties from result of GetProperties
  2624     DBusError err;
  2625     dbus_error_init(&err);
  2627     BluetoothValue deviceProperties;
  2629     bool success = UnpackPropertiesMessage(aReply, &err, deviceProperties,
  2630                                            DBUS_DEVICE_IFACE);
  2631     if (!success) {
  2632       BT_WARNING("Failed to get device properties");
  2633       ProcessRemainingDeviceAddresses();
  2634       return;
  2637     InfallibleTArray<BluetoothNamedValue>& devicePropertiesArray =
  2638       deviceProperties.get_ArrayOfBluetoothNamedValue();
  2640     // We have to manually attach the path to the rest of the elements
  2641     devicePropertiesArray.AppendElement(
  2642       BluetoothNamedValue(NS_LITERAL_STRING("Path"), mObjectPath));
  2644     // It is possible that property Icon missed due to CoD of major
  2645     // class is TOY but service class is "Audio", we need to assign
  2646     // Icon as audio-card. This is for PTS test TC_AG_COD_BV_02_I.
  2647     // As HFP specification defined that
  2648     // service class is "Audio" can be considered as HFP AG.
  2649     if (!ContainsIcon(devicePropertiesArray)) {
  2650       for (uint32_t j = 0; j < devicePropertiesArray.Length(); ++j) {
  2651         BluetoothNamedValue& deviceProperty = devicePropertiesArray[j];
  2652         if (deviceProperty.name().EqualsLiteral("Class")) {
  2653           if (HasAudioService(deviceProperty.value().get_uint32_t())) {
  2654             devicePropertiesArray.AppendElement(
  2655               BluetoothNamedValue(NS_LITERAL_STRING("Icon"),
  2656                                   NS_LITERAL_STRING("audio-card")));
  2658           break;
  2663     if (mFilterFunc(deviceProperties)) {
  2664       mValues.get_ArrayOfBluetoothNamedValue().AppendElement(
  2665         BluetoothNamedValue(mDeviceAddresses[i], deviceProperties));
  2668     ProcessRemainingDeviceAddresses();
  2671   void ProcessRemainingDeviceAddresses()
  2673     if (mProcessedDeviceAddresses < mDeviceAddresses.Length()) {
  2674       if (!SendNextGetProperties()) {
  2675         DispatchBluetoothReply(mRunnable, BluetoothValue(),
  2676                                NS_LITERAL_STRING(
  2677                                  "SendNextGetProperties failed"));
  2679     } else {
  2680       // Send resulting device properties
  2681       DispatchBluetoothReply(mRunnable, mValues, EmptyString());
  2685 protected:
  2686   bool SendNextGetProperties()
  2688     MOZ_ASSERT(mProcessedDeviceAddresses < mDeviceAddresses.Length());
  2689     MOZ_ASSERT(!sAdapterPath.IsEmpty());
  2690     MOZ_ASSERT(sDBusConnection);
  2692     // cache object path for reply
  2693     mObjectPath = GetObjectPathFromAddress(sAdapterPath,
  2694       mDeviceAddresses[mProcessedDeviceAddresses]);
  2696     nsRefPtr<BluetoothArrayOfDevicePropertiesReplyHandler> handler = this;
  2698     bool success = sDBusConnection->SendWithReply(
  2699       BluetoothArrayOfDevicePropertiesReplyHandler::Callback,
  2700       handler.get(), 1000, BLUEZ_DBUS_BASE_IFC,
  2701       NS_ConvertUTF16toUTF8(mObjectPath).get(),
  2702       DBUS_DEVICE_IFACE, "GetProperties",
  2703       DBUS_TYPE_INVALID);
  2705     NS_ENSURE_TRUE(success, false);
  2707     unused << handler.forget(); // picked up by callback handler
  2709     return true;
  2712 private:
  2713   nsString mObjectPath;
  2714   const nsTArray<nsString> mDeviceAddresses;
  2715   nsTArray<nsString>::size_type mProcessedDeviceAddresses;
  2716   const FilterFunc mFilterFunc;
  2717   nsRefPtr<BluetoothReplyRunnable> mRunnable;
  2718   BluetoothValue mValues;
  2719 };
  2721 class ProcessRemainingDeviceAddressesTask : public Task
  2723 public:
  2724   ProcessRemainingDeviceAddressesTask(
  2725     BluetoothArrayOfDevicePropertiesReplyHandler* aHandler,
  2726     BluetoothReplyRunnable* aRunnable)
  2727     : mHandler(aHandler)
  2728     , mRunnable(aRunnable)
  2730     MOZ_ASSERT(mHandler);
  2731     MOZ_ASSERT(mRunnable);
  2734   void Run() MOZ_OVERRIDE
  2736     MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
  2738     mHandler->ProcessRemainingDeviceAddresses();
  2741 private:
  2742   nsRefPtr<BluetoothArrayOfDevicePropertiesReplyHandler> mHandler;
  2743   nsRefPtr<BluetoothReplyRunnable> mRunnable;
  2744 };
  2746 nsresult
  2747 BluetoothDBusService::GetConnectedDevicePropertiesInternal(uint16_t aServiceUuid,
  2748                                               BluetoothReplyRunnable* aRunnable)
  2750   MOZ_ASSERT(NS_IsMainThread());
  2752   nsAutoString errorStr;
  2753   BluetoothValue values = InfallibleTArray<BluetoothNamedValue>();
  2754   if (!IsReady()) {
  2755     NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!");
  2756     DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr);
  2757     return NS_OK;
  2760   nsTArray<nsString> deviceAddresses;
  2761   BluetoothProfileManagerBase* profile =
  2762     BluetoothUuidHelper::GetBluetoothProfileManager(aServiceUuid);
  2763   if (!profile) {
  2764     DispatchBluetoothReply(aRunnable, values,
  2765                            NS_LITERAL_STRING(ERR_UNKNOWN_PROFILE));
  2766     return NS_OK;
  2769   if (profile->IsConnected()) {
  2770     nsString address;
  2771     profile->GetAddress(address);
  2772     deviceAddresses.AppendElement(address);
  2775   BluetoothArrayOfDevicePropertiesReplyHandler* handler =
  2776     new BluetoothArrayOfDevicePropertiesReplyHandler(deviceAddresses,
  2777                                                      GetConnectedDevicesFilter,
  2778                                                      aRunnable);
  2779   Task* task = new ProcessRemainingDeviceAddressesTask(handler, aRunnable);
  2780   DispatchToDBusThread(task);
  2782   return NS_OK;
  2785 nsresult
  2786 BluetoothDBusService::GetPairedDevicePropertiesInternal(
  2787                                      const nsTArray<nsString>& aDeviceAddresses,
  2788                                      BluetoothReplyRunnable* aRunnable)
  2790   MOZ_ASSERT(NS_IsMainThread());
  2792   if (!IsReady()) {
  2793     NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!");
  2794     DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr);
  2795     return NS_OK;
  2798   BluetoothArrayOfDevicePropertiesReplyHandler* handler =
  2799     new BluetoothArrayOfDevicePropertiesReplyHandler(aDeviceAddresses,
  2800                                                      GetPairedDevicesFilter,
  2801                                                      aRunnable);
  2802   Task* task = new ProcessRemainingDeviceAddressesTask(handler, aRunnable);
  2803   DispatchToDBusThread(task);
  2805   return NS_OK;
  2808 class SetPropertyTask : public Task
  2810 public:
  2811   SetPropertyTask(BluetoothObjectType aType,
  2812                   const nsACString& aName,
  2813                   BluetoothReplyRunnable* aRunnable)
  2814     : mType(aType)
  2815     , mName(aName)
  2816     , mRunnable(aRunnable)
  2818     MOZ_ASSERT(mRunnable);
  2821   void Send(unsigned int aType, const void* aValue)
  2823     MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
  2824     MOZ_ASSERT(sDBusConnection);
  2825     MOZ_ASSERT(!sAdapterPath.IsEmpty());
  2827     DBusMessage* msg =
  2828       dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC,
  2829                                    NS_ConvertUTF16toUTF8(sAdapterPath).get(),
  2830                                    sBluetoothDBusIfaces[mType],
  2831                                    "SetProperty");
  2832     if (!msg) {
  2833       BT_WARNING("Could not allocate D-Bus message object!");
  2834       return;
  2837     const char* name = mName.get();
  2838     if (!dbus_message_append_args(msg, DBUS_TYPE_STRING, &name,
  2839                                   DBUS_TYPE_INVALID)) {
  2840       BT_WARNING("Couldn't append arguments to dbus message!");
  2841       return;
  2844     DBusMessageIter value_iter, iter;
  2845     dbus_message_iter_init_append(msg, &iter);
  2846     char var_type[2] = {(char)aType, '\0'};
  2847     if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
  2848                                           var_type, &value_iter) ||
  2849         !dbus_message_iter_append_basic(&value_iter, aType, aValue) ||
  2850         !dbus_message_iter_close_container(&iter, &value_iter)) {
  2851       BT_WARNING("Could not append argument to method call!");
  2852       dbus_message_unref(msg);
  2853       return;
  2856     // msg is unref'd as part of SendWithReply
  2857     bool success = sDBusConnection->SendWithReply(
  2858       GetVoidCallback,
  2859       static_cast<void*>(mRunnable),
  2860       1000, msg);
  2861     NS_ENSURE_TRUE_VOID(success);
  2863     unused << mRunnable.forget(); // picked up by callback handler
  2866 private:
  2867   BluetoothObjectType mType;
  2868   const nsCString mName;
  2869   nsRefPtr<BluetoothReplyRunnable> mRunnable;
  2870 };
  2872 class SetUInt32PropertyTask : public SetPropertyTask
  2874 public:
  2875   SetUInt32PropertyTask(BluetoothObjectType aType,
  2876                         const nsACString& aName,
  2877                         uint32_t aValue,
  2878                         BluetoothReplyRunnable* aRunnable)
  2879     : SetPropertyTask(aType, aName, aRunnable)
  2880     , mValue(aValue)
  2881   { }
  2883   void Run() MOZ_OVERRIDE
  2885     Send(DBUS_TYPE_UINT32, &mValue);
  2888 private:
  2889   dbus_uint32_t mValue;
  2890 };
  2892 class SetStringPropertyTask : public SetPropertyTask
  2894 public:
  2895   SetStringPropertyTask(BluetoothObjectType aType,
  2896                         const nsACString& aName,
  2897                         const nsACString& aValue,
  2898                         BluetoothReplyRunnable* aRunnable)
  2899     : SetPropertyTask(aType, aName, aRunnable)
  2900     , mValue(aValue)
  2901   { }
  2903   void Run() MOZ_OVERRIDE
  2905     const char* value = mValue.get();
  2906     Send(DBUS_TYPE_STRING, &value);
  2909 private:
  2910   const nsCString mValue;
  2911 };
  2913 class SetBooleanPropertyTask : public SetPropertyTask
  2915 public:
  2916   SetBooleanPropertyTask(BluetoothObjectType aType,
  2917                          const nsACString& aName,
  2918                          dbus_bool_t aValue,
  2919                          BluetoothReplyRunnable* aRunnable)
  2920     : SetPropertyTask(aType, aName, aRunnable)
  2921     , mValue(aValue)
  2925   void Run() MOZ_OVERRIDE
  2927     Send(DBUS_TYPE_BOOLEAN, &mValue);
  2930 private:
  2931   dbus_bool_t mValue;
  2932 };
  2934 nsresult
  2935 BluetoothDBusService::SetProperty(BluetoothObjectType aType,
  2936                                   const BluetoothNamedValue& aValue,
  2937                                   BluetoothReplyRunnable* aRunnable)
  2939   MOZ_ASSERT(NS_IsMainThread());
  2941   if (!IsReady()) {
  2942     NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!");
  2943     DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr);
  2944     return NS_OK;
  2947   Task* task;
  2949   if (aValue.value().type() == BluetoothValue::Tuint32_t) {
  2950     task = new SetUInt32PropertyTask(aType,
  2951       NS_ConvertUTF16toUTF8(aValue.name()),
  2952       aValue.value().get_uint32_t(), aRunnable);
  2953   } else if (aValue.value().type() == BluetoothValue::TnsString) {
  2954     task = new SetStringPropertyTask(aType,
  2955       NS_ConvertUTF16toUTF8(aValue.name()),
  2956       NS_ConvertUTF16toUTF8(aValue.value().get_nsString()), aRunnable);
  2957   } else if (aValue.value().type() == BluetoothValue::Tbool) {
  2958     task = new SetBooleanPropertyTask(aType,
  2959       NS_ConvertUTF16toUTF8(aValue.name()),
  2960       aValue.value().get_bool(), aRunnable);
  2961   } else {
  2962     BT_WARNING("Property type not handled!");
  2963     return NS_ERROR_FAILURE;
  2965   DispatchToDBusThread(task);
  2967   return NS_OK;
  2970 class CreatePairedDeviceInternalTask : public Task
  2972 public:
  2973   CreatePairedDeviceInternalTask(const nsACString& aDeviceAddress,
  2974                                  int aTimeout,
  2975                                  BluetoothReplyRunnable* aRunnable)
  2976     : mDeviceAddress(aDeviceAddress)
  2977     , mTimeout(aTimeout)
  2978     , mRunnable(aRunnable)
  2980     MOZ_ASSERT(!mDeviceAddress.IsEmpty());
  2981     MOZ_ASSERT(mRunnable);
  2984   void Run() MOZ_OVERRIDE
  2986     MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
  2987     MOZ_ASSERT(sDBusConnection);
  2988     MOZ_ASSERT(!sAdapterPath.IsEmpty());
  2990     const char *deviceAddress = mDeviceAddress.get();
  2991     const char *deviceAgentPath = KEY_REMOTE_AGENT;
  2992     const char *capabilities = B2G_AGENT_CAPABILITIES;
  2994     // Then send CreatePairedDevice, it will register a temp device agent then
  2995     // unregister it after pairing process is over
  2996     bool success = sDBusConnection->SendWithReply(
  2997       GetObjectPathCallback, static_cast<void*>(mRunnable), mTimeout,
  2998       BLUEZ_DBUS_BASE_IFC,
  2999       NS_ConvertUTF16toUTF8(sAdapterPath).get(),
  3000       DBUS_ADAPTER_IFACE,
  3001       "CreatePairedDevice",
  3002       DBUS_TYPE_STRING, &deviceAddress,
  3003       DBUS_TYPE_OBJECT_PATH, &deviceAgentPath,
  3004       DBUS_TYPE_STRING, &capabilities,
  3005       DBUS_TYPE_INVALID);
  3006     NS_ENSURE_TRUE_VOID(success);
  3008     unused << mRunnable.forget(); // picked up by callback handler
  3010     /**
  3011      * FIXME: Bug 820274
  3013      * If the user turns off Bluetooth in the middle of pairing process,
  3014      * the callback function GetObjectPathCallback may still be called
  3015      * while enabling next time by dbus daemon. To prevent this from
  3016      * happening, added a flag to distinguish if Bluetooth has been
  3017      * turned off. Nevertheless, we need a check if there is a better
  3018      * solution.
  3020      * Please see Bug 818696 for more information.
  3021      */
  3022     sIsPairing++;
  3025 private:
  3026   const nsCString mDeviceAddress;
  3027   int mTimeout;
  3028   nsRefPtr<BluetoothReplyRunnable> mRunnable;
  3029 };
  3031 nsresult
  3032 BluetoothDBusService::CreatePairedDeviceInternal(
  3033                                               const nsAString& aDeviceAddress,
  3034                                               int aTimeout,
  3035                                               BluetoothReplyRunnable* aRunnable)
  3037   Task* task = new CreatePairedDeviceInternalTask(
  3038     NS_ConvertUTF16toUTF8(aDeviceAddress),
  3039     aTimeout, aRunnable);
  3040   DispatchToDBusThread(task);
  3042   return NS_OK;
  3045 class RemoveDeviceTask : public Task
  3047 public:
  3048   RemoveDeviceTask(const nsAString& aDeviceAddress,
  3049                    BluetoothReplyRunnable* aRunnable)
  3050     : mDeviceAddress(aDeviceAddress)
  3051     , mRunnable(aRunnable)
  3053     MOZ_ASSERT(!mDeviceAddress.IsEmpty());
  3054     MOZ_ASSERT(mRunnable);
  3057   void Run() MOZ_OVERRIDE
  3059     MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
  3060     MOZ_ASSERT(sDBusConnection);
  3061     MOZ_ASSERT(!sAdapterPath.IsEmpty());
  3063     nsCString deviceObjectPath =
  3064       NS_ConvertUTF16toUTF8(GetObjectPathFromAddress(sAdapterPath,
  3065                                                      mDeviceAddress));
  3066     const char* cstrDeviceObjectPath = deviceObjectPath.get();
  3068     bool success = sDBusConnection->SendWithReply(
  3069       OnRemoveDeviceReply, static_cast<void*>(mRunnable.get()), -1,
  3070       BLUEZ_DBUS_BASE_IFC,
  3071       NS_ConvertUTF16toUTF8(sAdapterPath).get(),
  3072       DBUS_ADAPTER_IFACE, "RemoveDevice",
  3073       DBUS_TYPE_OBJECT_PATH, &cstrDeviceObjectPath,
  3074       DBUS_TYPE_INVALID);
  3075     NS_ENSURE_TRUE_VOID(success);
  3077     unused << mRunnable.forget(); // picked up by callback handler
  3080 protected:
  3081   static void OnRemoveDeviceReply(DBusMessage* aReply, void* aData)
  3083     nsAutoString errorStr;
  3085     if (!aReply) {
  3086       errorStr.AssignLiteral("RemoveDevice failed");
  3089     nsRefPtr<BluetoothReplyRunnable> runnable =
  3090       dont_AddRef<BluetoothReplyRunnable>(
  3091         static_cast<BluetoothReplyRunnable*>(aData));
  3093     DispatchBluetoothReply(runnable.get(), BluetoothValue(true), errorStr);
  3096 private:
  3097   const nsString mDeviceAddress;
  3098   nsRefPtr<BluetoothReplyRunnable> mRunnable;
  3099 };
  3101 nsresult
  3102 BluetoothDBusService::RemoveDeviceInternal(const nsAString& aDeviceAddress,
  3103                                            BluetoothReplyRunnable* aRunnable)
  3105   MOZ_ASSERT(NS_IsMainThread());
  3107   if (!IsReady()) {
  3108     NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!");
  3109     DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr);
  3110     return NS_OK;
  3113   Task* task = new RemoveDeviceTask(aDeviceAddress, aRunnable);
  3114   DispatchToDBusThread(task);
  3116   return NS_OK;
  3119 class SetPinCodeTask : public Task
  3121 public:
  3122   SetPinCodeTask(const nsAString& aDeviceAddress,
  3123                  const nsACString& aPinCode,
  3124                  BluetoothReplyRunnable* aRunnable)
  3125     : mDeviceAddress(aDeviceAddress)
  3126     , mPinCode(aPinCode)
  3127     , mRunnable(aRunnable)
  3129     MOZ_ASSERT(!mDeviceAddress.IsEmpty());
  3130     MOZ_ASSERT(mRunnable);
  3133   void Run() MOZ_OVERRIDE
  3135     MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
  3137     nsAutoString errorStr;
  3138     BluetoothValue v = true;
  3139     DBusMessage *msg;
  3140     if (!sPairingReqTable->Get(mDeviceAddress, &msg)) {
  3141       BT_WARNING("%s: Couldn't get original request message.", __FUNCTION__);
  3142       errorStr.AssignLiteral("Couldn't get original request message.");
  3143       DispatchBluetoothReply(mRunnable, v, errorStr);
  3144       return;
  3147     DBusMessage *reply = dbus_message_new_method_return(msg);
  3149     if (!reply) {
  3150       BT_WARNING("%s: Memory can't be allocated for the message.", __FUNCTION__);
  3151       dbus_message_unref(msg);
  3152       errorStr.AssignLiteral("Memory can't be allocated for the message.");
  3153       DispatchBluetoothReply(mRunnable, v, errorStr);
  3154       return;
  3157     const char* pinCode = mPinCode.get();
  3159     if (!dbus_message_append_args(reply,
  3160                                   DBUS_TYPE_STRING, &pinCode,
  3161                                   DBUS_TYPE_INVALID)) {
  3162       BT_WARNING("%s: Couldn't append arguments to dbus message.", __FUNCTION__);
  3163       errorStr.AssignLiteral("Couldn't append arguments to dbus message.");
  3164     } else {
  3165       MOZ_ASSERT(sDBusConnection);
  3166       sDBusConnection->Send(reply);
  3169     dbus_message_unref(msg);
  3170     dbus_message_unref(reply);
  3172     sPairingReqTable->Remove(mDeviceAddress);
  3173     DispatchBluetoothReply(mRunnable, v, errorStr);
  3176 private:
  3177   const nsString mDeviceAddress;
  3178   const nsCString mPinCode;
  3179   nsRefPtr<BluetoothReplyRunnable> mRunnable;
  3180 };
  3182 bool
  3183 BluetoothDBusService::SetPinCodeInternal(const nsAString& aDeviceAddress,
  3184                                          const nsAString& aPinCode,
  3185                                          BluetoothReplyRunnable* aRunnable)
  3187   Task* task = new SetPinCodeTask(aDeviceAddress,
  3188                                   NS_ConvertUTF16toUTF8(aPinCode),
  3189                                   aRunnable);
  3190   DispatchToDBusThread(task);
  3192   return true;
  3195 class SetPasskeyTask : public Task
  3197 public:
  3198   SetPasskeyTask(const nsAString& aDeviceAddress,
  3199                  uint32_t aPasskey,
  3200                  BluetoothReplyRunnable* aRunnable)
  3201     : mDeviceAddress(aDeviceAddress)
  3202     , mPasskey(aPasskey)
  3203     , mRunnable(aRunnable)
  3205     MOZ_ASSERT(!mDeviceAddress.IsEmpty());
  3206     MOZ_ASSERT(mRunnable);
  3209   void Run() MOZ_OVERRIDE
  3211     MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
  3213     nsAutoString errorStr;
  3214     BluetoothValue v = true;
  3215     DBusMessage *msg;
  3216     if (!sPairingReqTable->Get(mDeviceAddress, &msg)) {
  3217       BT_WARNING("%s: Couldn't get original request message.", __FUNCTION__);
  3218       errorStr.AssignLiteral("Couldn't get original request message.");
  3219       DispatchBluetoothReply(mRunnable, v, errorStr);
  3220       return;
  3223     DBusMessage *reply = dbus_message_new_method_return(msg);
  3225     if (!reply) {
  3226       BT_WARNING("%s: Memory can't be allocated for the message.", __FUNCTION__);
  3227       dbus_message_unref(msg);
  3228       errorStr.AssignLiteral("Memory can't be allocated for the message.");
  3229       DispatchBluetoothReply(mRunnable, v, errorStr);
  3230       return;
  3233     uint32_t passkey = mPasskey;
  3235     if (!dbus_message_append_args(reply,
  3236                                   DBUS_TYPE_UINT32, &passkey,
  3237                                   DBUS_TYPE_INVALID)) {
  3238       BT_WARNING("%s: Couldn't append arguments to dbus message.", __FUNCTION__);
  3239       errorStr.AssignLiteral("Couldn't append arguments to dbus message.");
  3240     } else {
  3241       MOZ_ASSERT(sDBusConnection);
  3242       sDBusConnection->Send(reply);
  3245     dbus_message_unref(msg);
  3246     dbus_message_unref(reply);
  3248     sPairingReqTable->Remove(mDeviceAddress);
  3249     DispatchBluetoothReply(mRunnable, v, errorStr);
  3252 private:
  3253   nsString mDeviceAddress;
  3254   uint32_t mPasskey;
  3255   nsRefPtr<BluetoothReplyRunnable> mRunnable;
  3256 };
  3258 bool
  3259 BluetoothDBusService::SetPasskeyInternal(const nsAString& aDeviceAddress,
  3260                                          uint32_t aPasskey,
  3261                                          BluetoothReplyRunnable* aRunnable)
  3263   Task* task = new SetPasskeyTask(aDeviceAddress,
  3264                                   aPasskey,
  3265                                   aRunnable);
  3266   DispatchToDBusThread(task);
  3268   return true;
  3272 bool
  3273 BluetoothDBusService::SetPairingConfirmationInternal(
  3274                                               const nsAString& aDeviceAddress,
  3275                                               bool aConfirm,
  3276                                               BluetoothReplyRunnable* aRunnable)
  3278   MOZ_ASSERT(NS_IsMainThread());
  3280   Task* task = new SetPairingConfirmationTask(aDeviceAddress,
  3281                                               aConfirm,
  3282                                               aRunnable);
  3283   DispatchToDBusThread(task);
  3285   return true;
  3288 static void
  3289 NextBluetoothProfileController()
  3291   MOZ_ASSERT(NS_IsMainThread());
  3293   // First, remove the task at the front which has been already done.
  3294   NS_ENSURE_FALSE_VOID(sControllerArray.IsEmpty());
  3295   sControllerArray.RemoveElementAt(0);
  3297   // Re-check if the task array is empty, if it's not, the next task will begin.
  3298   NS_ENSURE_FALSE_VOID(sControllerArray.IsEmpty());
  3299   sControllerArray[0]->StartSession();
  3302 static void
  3303 ConnectDisconnect(bool aConnect, const nsAString& aDeviceAddress,
  3304                   BluetoothReplyRunnable* aRunnable,
  3305                   uint16_t aServiceUuid, uint32_t aCod = 0)
  3307   MOZ_ASSERT(NS_IsMainThread());
  3308   MOZ_ASSERT(aRunnable);
  3310   BluetoothProfileController* controller =
  3311     new BluetoothProfileController(aConnect, aDeviceAddress, aRunnable,
  3312                                    NextBluetoothProfileController,
  3313                                    aServiceUuid, aCod);
  3314   sControllerArray.AppendElement(controller);
  3316   /**
  3317    * If the request is the first element of the quene, start from here. Note
  3318    * that other request is pushed into the quene and is popped out after the
  3319    * first one is completed. See NextBluetoothProfileController() for details.
  3320    */
  3321   if (sControllerArray.Length() == 1) {
  3322     sControllerArray[0]->StartSession();
  3326 void
  3327 BluetoothDBusService::Connect(const nsAString& aDeviceAddress,
  3328                               uint32_t aCod,
  3329                               uint16_t aServiceUuid,
  3330                               BluetoothReplyRunnable* aRunnable)
  3332   ConnectDisconnect(true, aDeviceAddress, aRunnable, aServiceUuid, aCod);
  3335 void
  3336 BluetoothDBusService::Disconnect(const nsAString& aDeviceAddress,
  3337                                  uint16_t aServiceUuid,
  3338                                  BluetoothReplyRunnable* aRunnable)
  3340   ConnectDisconnect(false, aDeviceAddress, aRunnable, aServiceUuid);
  3343 bool
  3344 BluetoothDBusService::IsConnected(const uint16_t aServiceUuid)
  3346   MOZ_ASSERT(NS_IsMainThread());
  3348   BluetoothProfileManagerBase* profile =
  3349     BluetoothUuidHelper::GetBluetoothProfileManager(aServiceUuid);
  3350   if (!profile) {
  3351     BT_WARNING(ERR_UNKNOWN_PROFILE);
  3352     return false;
  3355   NS_ENSURE_TRUE(profile, false);
  3356   return profile->IsConnected();
  3359 #ifdef MOZ_B2G_RIL
  3360 void
  3361 BluetoothDBusService::AnswerWaitingCall(BluetoothReplyRunnable* aRunnable)
  3363   MOZ_ASSERT(NS_IsMainThread());
  3365   BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
  3366   hfp->AnswerWaitingCall();
  3368   DispatchBluetoothReply(aRunnable, BluetoothValue(true), EmptyString());
  3371 void
  3372 BluetoothDBusService::IgnoreWaitingCall(BluetoothReplyRunnable* aRunnable)
  3374   MOZ_ASSERT(NS_IsMainThread());
  3376   BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
  3377   hfp->IgnoreWaitingCall();
  3379   DispatchBluetoothReply(aRunnable, BluetoothValue(true), EmptyString());
  3382 void
  3383 BluetoothDBusService::ToggleCalls(BluetoothReplyRunnable* aRunnable)
  3385   MOZ_ASSERT(NS_IsMainThread());
  3387   BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
  3388   hfp->ToggleCalls();
  3390   DispatchBluetoothReply(aRunnable, BluetoothValue(true), EmptyString());
  3392 #endif // MOZ_B2G_RIL
  3394 class OnUpdateSdpRecordsRunnable : public nsRunnable
  3396 public:
  3397   OnUpdateSdpRecordsRunnable(const nsAString& aObjectPath,
  3398                              BluetoothProfileManagerBase* aManager)
  3399     : mManager(aManager)
  3401     MOZ_ASSERT(!aObjectPath.IsEmpty());
  3402     MOZ_ASSERT(aManager);
  3404     mDeviceAddress = GetAddressFromObjectPath(aObjectPath);
  3407   nsresult
  3408   Run()
  3410     MOZ_ASSERT(NS_IsMainThread());
  3412     mManager->OnUpdateSdpRecords(mDeviceAddress);
  3414     return NS_OK;
  3417 private:
  3418   nsString mDeviceAddress;
  3419   BluetoothProfileManagerBase* mManager;
  3420 };
  3422 class OnGetServiceChannelRunnable : public nsRunnable
  3424 public:
  3425   OnGetServiceChannelRunnable(const nsAString& aDeviceAddress,
  3426                               const nsAString& aServiceUuid,
  3427                               int aChannel,
  3428                               BluetoothProfileManagerBase* aManager)
  3429     : mDeviceAddress(aDeviceAddress)
  3430     , mServiceUuid(aServiceUuid)
  3431     , mChannel(aChannel)
  3432     , mManager(aManager)
  3434     MOZ_ASSERT(!aDeviceAddress.IsEmpty());
  3435     MOZ_ASSERT(!aServiceUuid.IsEmpty());
  3436     MOZ_ASSERT(aManager);
  3439   NS_IMETHOD Run()
  3441     MOZ_ASSERT(NS_IsMainThread());
  3443     mManager->OnGetServiceChannel(mDeviceAddress, mServiceUuid, mChannel);
  3445     return NS_OK;
  3448 private:
  3449   nsString mDeviceAddress;
  3450   nsString mServiceUuid;
  3451   int mChannel;
  3452   BluetoothProfileManagerBase* mManager;
  3453 };
  3455 class OnGetServiceChannelReplyHandler : public DBusReplyHandler
  3457 public:
  3458   OnGetServiceChannelReplyHandler(const nsAString& aDeviceAddress,
  3459                                   const nsAString& aServiceUUID,
  3460                                   BluetoothProfileManagerBase* aBluetoothProfileManager)
  3461   : mDeviceAddress(aDeviceAddress),
  3462     mServiceUUID(aServiceUUID),
  3463     mBluetoothProfileManager(aBluetoothProfileManager)
  3465     MOZ_ASSERT(mBluetoothProfileManager);
  3468   void Handle(DBusMessage* aReply)
  3470     MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
  3472     // The default channel is an invalid value of -1. We
  3473     // update it if we have received a correct reply. Both
  3474     // cases, valid and invalid channel numbers, are handled
  3475     // in BluetoothProfileManagerBase::OnGetServiceChannel.
  3477     int channel = -1;
  3479     if (aReply && (dbus_message_get_type(aReply) != DBUS_MESSAGE_TYPE_ERROR)) {
  3480       channel = dbus_returns_int32(aReply);
  3483     nsRefPtr<nsRunnable> r = new OnGetServiceChannelRunnable(mDeviceAddress,
  3484                                                              mServiceUUID,
  3485                                                              channel,
  3486                                                              mBluetoothProfileManager);
  3487     nsresult rv = NS_DispatchToMainThread(r);
  3488     NS_ENSURE_SUCCESS_VOID(rv);
  3491 private:
  3492   nsString mDeviceAddress;
  3493   nsString mServiceUUID;
  3494   BluetoothProfileManagerBase* mBluetoothProfileManager;
  3495 };
  3497 class GetServiceChannelTask : public Task
  3499 public:
  3500   GetServiceChannelTask(const nsAString& aDeviceAddress,
  3501                         const nsAString& aServiceUUID,
  3502                         BluetoothProfileManagerBase* aBluetoothProfileManager)
  3503     : mDeviceAddress(aDeviceAddress)
  3504     , mServiceUUID(aServiceUUID)
  3505     , mBluetoothProfileManager(aBluetoothProfileManager)
  3507     MOZ_ASSERT(!mDeviceAddress.IsEmpty());
  3508     MOZ_ASSERT(mBluetoothProfileManager);
  3511   void Run() MOZ_OVERRIDE
  3513     static const int sProtocolDescriptorList = 0x0004;
  3515     MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
  3516     MOZ_ASSERT(sDBusConnection);
  3517     MOZ_ASSERT(!sAdapterPath.IsEmpty());
  3519     nsString objectPath =
  3520       GetObjectPathFromAddress(sAdapterPath, mDeviceAddress);
  3522     nsRefPtr<OnGetServiceChannelReplyHandler> handler =
  3523       new OnGetServiceChannelReplyHandler(mDeviceAddress, mServiceUUID,
  3524                                           mBluetoothProfileManager);
  3526     nsCString serviceUUID = NS_ConvertUTF16toUTF8(mServiceUUID);
  3527     const char* cstrServiceUUID = serviceUUID.get();
  3529     bool success = sDBusConnection->SendWithReply(
  3530       OnGetServiceChannelReplyHandler::Callback, handler, -1,
  3531       BLUEZ_DBUS_BASE_IFC,
  3532       NS_ConvertUTF16toUTF8(objectPath).get(),
  3533       DBUS_DEVICE_IFACE, "GetServiceAttributeValue",
  3534       DBUS_TYPE_STRING, &cstrServiceUUID,
  3535       DBUS_TYPE_UINT16, &sProtocolDescriptorList,
  3536       DBUS_TYPE_INVALID);
  3537     NS_ENSURE_TRUE_VOID(success);
  3539     unused << handler.forget(); // picked up by callback handler
  3542 private:
  3543   nsString mDeviceAddress;
  3544   nsString mServiceUUID;
  3545   BluetoothProfileManagerBase* mBluetoothProfileManager;
  3546 };
  3548 nsresult
  3549 BluetoothDBusService::GetServiceChannel(const nsAString& aDeviceAddress,
  3550                                         const nsAString& aServiceUUID,
  3551                                         BluetoothProfileManagerBase* aManager)
  3553   MOZ_ASSERT(NS_IsMainThread());
  3555   if (!IsReady()) {
  3556     NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!");
  3557     return NS_OK;
  3560 #ifdef MOZ_WIDGET_GONK
  3561   // GetServiceAttributeValue only exists in android's bluez dbus binding
  3562   // implementation
  3563   Task* task = new GetServiceChannelTask(aDeviceAddress,
  3564                                          aServiceUUID,
  3565                                          aManager);
  3566   DispatchToDBusThread(task);
  3567 #else
  3568   // FIXME/Bug 793977 qdot: Just set something for desktop, until we have a
  3569   // parser for the GetServiceAttributes xml block
  3570   //
  3571   // Even though we are on the main thread already, we need to dispatch a
  3572   // runnable here. OnGetServiceChannel needs mRunnable to be set, which
  3573   // happens after GetServiceChannel returns.
  3574   nsRefPtr<nsRunnable> r = new OnGetServiceChannelRunnable(aDeviceAddress,
  3575                                                            aServiceUUID,
  3576                                                            1,
  3577                                                            aManager);
  3578   NS_DispatchToMainThread(r);
  3579 #endif
  3581   return NS_OK;
  3584 class UpdateSdpRecordsTask : public Task
  3586 public:
  3587   UpdateSdpRecordsTask(const nsAString& aDeviceAddress,
  3588                        BluetoothProfileManagerBase* aBluetoothProfileManager)
  3589     : mDeviceAddress(aDeviceAddress)
  3590     , mBluetoothProfileManager(aBluetoothProfileManager)
  3592     MOZ_ASSERT(!mDeviceAddress.IsEmpty());
  3593     MOZ_ASSERT(mBluetoothProfileManager);
  3596   void Run() MOZ_OVERRIDE
  3598     MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
  3599     MOZ_ASSERT(sDBusConnection);
  3600     MOZ_ASSERT(!sAdapterPath.IsEmpty());
  3602     const nsString objectPath =
  3603       GetObjectPathFromAddress(sAdapterPath, mDeviceAddress);
  3605     // I choose to use raw pointer here because this is going to be passed as an
  3606     // argument into SendWithReply() at once.
  3607     OnUpdateSdpRecordsRunnable* callbackRunnable =
  3608       new OnUpdateSdpRecordsRunnable(objectPath, mBluetoothProfileManager);
  3610     sDBusConnection->SendWithReply(DiscoverServicesCallback,
  3611                                    (void*)callbackRunnable, -1,
  3612                                    BLUEZ_DBUS_BASE_IFC,
  3613                                    NS_ConvertUTF16toUTF8(objectPath).get(),
  3614                                    DBUS_DEVICE_IFACE,
  3615                                    "DiscoverServices",
  3616                                    DBUS_TYPE_STRING, &EmptyCString(),
  3617                                    DBUS_TYPE_INVALID);
  3620 protected:
  3621   static void DiscoverServicesCallback(DBusMessage* aMsg, void* aData)
  3623     MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
  3625     nsRefPtr<OnUpdateSdpRecordsRunnable> r(
  3626       static_cast<OnUpdateSdpRecordsRunnable*>(aData));
  3627     NS_DispatchToMainThread(r);
  3630 private:
  3631   const nsString mDeviceAddress;
  3632   BluetoothProfileManagerBase* mBluetoothProfileManager;
  3633 };
  3635 bool
  3636 BluetoothDBusService::UpdateSdpRecords(const nsAString& aDeviceAddress,
  3637                                        BluetoothProfileManagerBase* aManager)
  3639   MOZ_ASSERT(NS_IsMainThread());
  3641   Task* task = new UpdateSdpRecordsTask(aDeviceAddress, aManager);
  3642   DispatchToDBusThread(task);
  3644   return true;
  3647 void
  3648 BluetoothDBusService::SendFile(const nsAString& aDeviceAddress,
  3649                                BlobParent* aBlobParent,
  3650                                BlobChild* aBlobChild,
  3651                                BluetoothReplyRunnable* aRunnable)
  3653   MOZ_ASSERT(NS_IsMainThread());
  3655   // Currently we only support one device sending one file at a time,
  3656   // so we don't need aDeviceAddress here because the target device
  3657   // has been determined when calling 'Connect()'. Nevertheless, keep
  3658   // it for future use.
  3659   BluetoothOppManager* opp = BluetoothOppManager::Get();
  3660   nsAutoString errorStr;
  3661   if (!opp || !opp->SendFile(aDeviceAddress, aBlobParent)) {
  3662     errorStr.AssignLiteral("Calling SendFile() failed");
  3665   DispatchBluetoothReply(aRunnable, BluetoothValue(true), errorStr);
  3668 void
  3669 BluetoothDBusService::SendFile(const nsAString& aDeviceAddress,
  3670                                nsIDOMBlob* aBlob,
  3671                                BluetoothReplyRunnable* aRunnable)
  3673   MOZ_ASSERT(NS_IsMainThread());
  3675   // Currently we only support one device sending one file at a time,
  3676   // so we don't need aDeviceAddress here because the target device
  3677   // has been determined when calling 'Connect()'. Nevertheless, keep
  3678   // it for future use.
  3679   BluetoothOppManager* opp = BluetoothOppManager::Get();
  3680   nsAutoString errorStr;
  3681   if (!opp || !opp->SendFile(aDeviceAddress, aBlob)) {
  3682     errorStr.AssignLiteral("Calling SendFile() failed");
  3685   DispatchBluetoothReply(aRunnable, BluetoothValue(true), errorStr);
  3688 void
  3689 BluetoothDBusService::StopSendingFile(const nsAString& aDeviceAddress,
  3690                                       BluetoothReplyRunnable* aRunnable)
  3692   MOZ_ASSERT(NS_IsMainThread());
  3694   // Currently we only support one device sending one file at a time,
  3695   // so we don't need aDeviceAddress here because the target device
  3696   // has been determined when calling 'Connect()'. Nevertheless, keep
  3697   // it for future use.
  3698   BluetoothOppManager* opp = BluetoothOppManager::Get();
  3699   nsAutoString errorStr;
  3700   if (!opp || !opp->StopSendingFile()) {
  3701     errorStr.AssignLiteral("Calling StopSendingFile() failed");
  3704   DispatchBluetoothReply(aRunnable, BluetoothValue(true), errorStr);
  3707 void
  3708 BluetoothDBusService::ConfirmReceivingFile(const nsAString& aDeviceAddress,
  3709                                            bool aConfirm,
  3710                                            BluetoothReplyRunnable* aRunnable)
  3712   MOZ_ASSERT(NS_IsMainThread(), "Must be called from main thread!");
  3714   // Currently we only support one device sending one file at a time,
  3715   // so we don't need aDeviceAddress here because the target device
  3716   // has been determined when calling 'Connect()'. Nevertheless, keep
  3717   // it for future use.
  3718   BluetoothOppManager* opp = BluetoothOppManager::Get();
  3719   nsAutoString errorStr;
  3720   if (!opp || !opp->ConfirmReceivingFile(aConfirm)) {
  3721     errorStr.AssignLiteral("Calling ConfirmReceivingFile() failed");
  3724   DispatchBluetoothReply(aRunnable, BluetoothValue(true), errorStr);
  3727 void
  3728 BluetoothDBusService::ConnectSco(BluetoothReplyRunnable* aRunnable)
  3730   MOZ_ASSERT(NS_IsMainThread());
  3732   BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
  3733   if (!hfp || !hfp->ConnectSco(aRunnable)) {
  3734     NS_NAMED_LITERAL_STRING(replyError, "Calling ConnectSco() failed");
  3735     DispatchBluetoothReply(aRunnable, BluetoothValue(), replyError);
  3739 void
  3740 BluetoothDBusService::DisconnectSco(BluetoothReplyRunnable* aRunnable)
  3742   MOZ_ASSERT(NS_IsMainThread());
  3744   BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
  3745   if (!hfp || !hfp->DisconnectSco()) {
  3746     NS_NAMED_LITERAL_STRING(replyError, "Calling DisconnectSco() failed");
  3747     DispatchBluetoothReply(aRunnable, BluetoothValue(), replyError);
  3748     return;
  3751   DispatchBluetoothReply(aRunnable, BluetoothValue(true), EmptyString());
  3754 void
  3755 BluetoothDBusService::IsScoConnected(BluetoothReplyRunnable* aRunnable)
  3757   MOZ_ASSERT(NS_IsMainThread());
  3759   BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
  3760   if (!hfp) {
  3761     NS_NAMED_LITERAL_STRING(replyError, "Fail to get BluetoothHfpManager");
  3762     DispatchBluetoothReply(aRunnable, BluetoothValue(), replyError);
  3763     return;
  3766   DispatchBluetoothReply(aRunnable, hfp->IsScoConnected(), EmptyString());
  3769 class SendMetadataTask : public Task
  3771 public:
  3772   SendMetadataTask(const nsAString& aDeviceAddress,
  3773                    const nsACString& aTitle,
  3774                    const nsACString& aArtist,
  3775                    const nsACString& aAlbum,
  3776                    int64_t aMediaNumber,
  3777                    int64_t aTotalMediaCount,
  3778                    int64_t aDuration,
  3779                    BluetoothReplyRunnable* aRunnable)
  3780     : mDeviceAddress(aDeviceAddress)
  3781     , mTitle(aTitle)
  3782     , mArtist(aArtist)
  3783     , mAlbum(aAlbum)
  3784     , mMediaNumber(aMediaNumber)
  3785     , mTotalMediaCount(aTotalMediaCount)
  3786     , mDuration(aDuration)
  3787     , mRunnable(aRunnable)
  3789     MOZ_ASSERT(!mDeviceAddress.IsEmpty());
  3790     MOZ_ASSERT(mRunnable);
  3793   void Run() MOZ_OVERRIDE
  3795     MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
  3796     MOZ_ASSERT(sDBusConnection);
  3797     MOZ_ASSERT(!sAdapterPath.IsEmpty());
  3799     // We currently don't support genre field in music player.
  3800     // In order to send media metadata through AVRCP, we set genre to an empty
  3801     // string to match the BlueZ method "UpdateMetaData" with signature "sssssss",
  3802     // which takes genre field as the last parameter.
  3803     nsCString tempGenre = EmptyCString();
  3804     nsCString tempMediaNumber = EmptyCString();
  3805     nsCString tempTotalMediaCount = EmptyCString();
  3806     nsCString tempDuration = EmptyCString();
  3808     if (mMediaNumber >= 0) {
  3809       tempMediaNumber.AppendInt(mMediaNumber);
  3811     if (mTotalMediaCount >= 0) {
  3812       tempTotalMediaCount.AppendInt(mTotalMediaCount);
  3814     if (mDuration >= 0) {
  3815       tempDuration.AppendInt(mDuration);
  3818     const nsCString objectPath = NS_ConvertUTF16toUTF8(
  3819       GetObjectPathFromAddress(sAdapterPath, mDeviceAddress));
  3821     const char* title = mTitle.get();
  3822     const char* album = mAlbum.get();
  3823     const char* artist = mArtist.get();
  3824     const char* mediaNumber = tempMediaNumber.get();
  3825     const char* totalMediaCount = tempTotalMediaCount.get();
  3826     const char* duration = tempDuration.get();
  3827     const char* genre = tempGenre.get();
  3829     bool success = sDBusConnection->SendWithReply(
  3830       GetVoidCallback, static_cast<void*>(mRunnable.get()), -1,
  3831       BLUEZ_DBUS_BASE_IFC,
  3832       objectPath.get(),
  3833       DBUS_CTL_IFACE, "UpdateMetaData",
  3834       DBUS_TYPE_STRING, &title,
  3835       DBUS_TYPE_STRING, &artist,
  3836       DBUS_TYPE_STRING, &album,
  3837       DBUS_TYPE_STRING, &mediaNumber,
  3838       DBUS_TYPE_STRING, &totalMediaCount,
  3839       DBUS_TYPE_STRING, &duration,
  3840       DBUS_TYPE_STRING, &genre,
  3841       DBUS_TYPE_INVALID);
  3842     NS_ENSURE_TRUE_VOID(success);
  3844     unused << mRunnable.forget(); // picked up by callback handler
  3847 private:
  3848   const nsString mDeviceAddress;
  3849   const nsCString mTitle;
  3850   const nsCString mArtist;
  3851   const nsCString mAlbum;
  3852   int64_t mMediaNumber;
  3853   int64_t mTotalMediaCount;
  3854   int64_t mDuration;
  3855   nsRefPtr<BluetoothReplyRunnable> mRunnable;
  3856 };
  3858 void
  3859 BluetoothDBusService::SendMetaData(const nsAString& aTitle,
  3860                                    const nsAString& aArtist,
  3861                                    const nsAString& aAlbum,
  3862                                    int64_t aMediaNumber,
  3863                                    int64_t aTotalMediaCount,
  3864                                    int64_t aDuration,
  3865                                    BluetoothReplyRunnable* aRunnable)
  3867   MOZ_ASSERT(NS_IsMainThread());
  3869   if (!IsReady()) {
  3870     NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!");
  3871     DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr);
  3872     return;
  3875   BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get();
  3876   NS_ENSURE_TRUE_VOID(a2dp);
  3878   if (!a2dp->IsConnected()) {
  3879     DispatchBluetoothReply(aRunnable, BluetoothValue(),
  3880                            NS_LITERAL_STRING(ERR_A2DP_IS_DISCONNECTED));
  3881     return;
  3882   } else if (!a2dp->IsAvrcpConnected()) {
  3883     DispatchBluetoothReply(aRunnable, BluetoothValue(),
  3884                            NS_LITERAL_STRING(ERR_AVRCP_IS_DISCONNECTED));
  3885     return;
  3888   nsAutoString prevTitle, prevAlbum;
  3889   a2dp->GetTitle(prevTitle);
  3890   a2dp->GetAlbum(prevAlbum);
  3892   if (aMediaNumber != a2dp->GetMediaNumber() ||
  3893       !aTitle.Equals(prevTitle) ||
  3894       !aAlbum.Equals(prevAlbum)) {
  3895     UpdateNotification(ControlEventId::EVENT_TRACK_CHANGED, aMediaNumber);
  3898   nsAutoString deviceAddress;
  3899   a2dp->GetAddress(deviceAddress);
  3901   Task* task = new SendMetadataTask(
  3902     deviceAddress,
  3903     NS_ConvertUTF16toUTF8(aTitle),
  3904     NS_ConvertUTF16toUTF8(aArtist),
  3905     NS_ConvertUTF16toUTF8(aAlbum),
  3906     aMediaNumber,
  3907     aTotalMediaCount,
  3908     aDuration,
  3909     aRunnable);
  3910   DispatchToDBusThread(task);
  3912   a2dp->UpdateMetaData(aTitle, aArtist, aAlbum,
  3913                        aMediaNumber, aTotalMediaCount, aDuration);
  3916 static ControlPlayStatus
  3917 PlayStatusStringToControlPlayStatus(const nsAString& aPlayStatus)
  3919   ControlPlayStatus playStatus = ControlPlayStatus::PLAYSTATUS_UNKNOWN;
  3920   if (aPlayStatus.EqualsLiteral("STOPPED")) {
  3921     playStatus = ControlPlayStatus::PLAYSTATUS_STOPPED;
  3922   } else if (aPlayStatus.EqualsLiteral("PLAYING")) {
  3923     playStatus = ControlPlayStatus::PLAYSTATUS_PLAYING;
  3924   } else if (aPlayStatus.EqualsLiteral("PAUSED")) {
  3925     playStatus = ControlPlayStatus::PLAYSTATUS_PAUSED;
  3926   } else if (aPlayStatus.EqualsLiteral("FWD_SEEK")) {
  3927     playStatus = ControlPlayStatus::PLAYSTATUS_FWD_SEEK;
  3928   } else if (aPlayStatus.EqualsLiteral("REV_SEEK")) {
  3929     playStatus = ControlPlayStatus::PLAYSTATUS_REV_SEEK;
  3930   } else if (aPlayStatus.EqualsLiteral("ERROR")) {
  3931     playStatus = ControlPlayStatus::PLAYSTATUS_ERROR;
  3934   return playStatus;
  3937 class SendPlayStatusTask : public Task
  3939 public:
  3940   SendPlayStatusTask(const nsAString& aDeviceAddress,
  3941                      int64_t aDuration,
  3942                      int64_t aPosition,
  3943                      ControlPlayStatus aPlayStatus,
  3944                      BluetoothReplyRunnable* aRunnable)
  3945     : mDeviceAddress(aDeviceAddress)
  3946     , mDuration(aDuration)
  3947     , mPosition(aPosition)
  3948     , mPlayStatus(aPlayStatus)
  3949     , mRunnable(aRunnable)
  3951     MOZ_ASSERT(!mDeviceAddress.IsEmpty());
  3952     MOZ_ASSERT(mRunnable);
  3955   void Run() MOZ_OVERRIDE
  3957     MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
  3958     MOZ_ASSERT(sDBusConnection);
  3959     MOZ_ASSERT(!sAdapterPath.IsEmpty());
  3961     const nsCString objectPath = NS_ConvertUTF16toUTF8(
  3962       GetObjectPathFromAddress(sAdapterPath, mDeviceAddress));
  3964     uint32_t tempPlayStatus = mPlayStatus;
  3966     bool success = sDBusConnection->SendWithReply(
  3967       GetVoidCallback, static_cast<void*>(mRunnable.get()), -1,
  3968       BLUEZ_DBUS_BASE_IFC,
  3969       objectPath.get(),
  3970       DBUS_CTL_IFACE, "UpdatePlayStatus",
  3971       DBUS_TYPE_UINT32, &mDuration,
  3972       DBUS_TYPE_UINT32, &mPosition,
  3973       DBUS_TYPE_UINT32, &tempPlayStatus,
  3974       DBUS_TYPE_INVALID);
  3975     NS_ENSURE_TRUE_VOID(success);
  3977     unused << mRunnable.forget(); // picked up by callback handler
  3980 private:
  3981   const nsString mDeviceAddress;
  3982   int64_t mDuration;
  3983   int64_t mPosition;
  3984   ControlPlayStatus mPlayStatus;
  3985   nsRefPtr<BluetoothReplyRunnable> mRunnable;
  3986 };
  3988 void
  3989 BluetoothDBusService::SendPlayStatus(int64_t aDuration,
  3990                                      int64_t aPosition,
  3991                                      const nsAString& aPlayStatus,
  3992                                      BluetoothReplyRunnable* aRunnable)
  3994   MOZ_ASSERT(NS_IsMainThread());
  3996   if (!IsReady()) {
  3997     NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!");
  3998     DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr);
  3999     return;
  4002   ControlPlayStatus playStatus =
  4003     PlayStatusStringToControlPlayStatus(aPlayStatus);
  4004   if (playStatus == ControlPlayStatus::PLAYSTATUS_UNKNOWN) {
  4005     DispatchBluetoothReply(aRunnable, BluetoothValue(),
  4006                            NS_LITERAL_STRING("Invalid play status"));
  4007     return;
  4008   } else if (aDuration < 0) {
  4009     DispatchBluetoothReply(aRunnable, BluetoothValue(),
  4010                            NS_LITERAL_STRING("Invalid duration"));
  4011     return;
  4012   } else if (aPosition < 0) {
  4013     DispatchBluetoothReply(aRunnable, BluetoothValue(),
  4014                            NS_LITERAL_STRING("Invalid position"));
  4015     return;
  4018   BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get();
  4019   NS_ENSURE_TRUE_VOID(a2dp);
  4021   if (!a2dp->IsConnected()) {
  4022     DispatchBluetoothReply(aRunnable, BluetoothValue(),
  4023                            NS_LITERAL_STRING(ERR_A2DP_IS_DISCONNECTED));
  4024     return;
  4025   } else if (!a2dp->IsAvrcpConnected()) {
  4026     DispatchBluetoothReply(aRunnable, BluetoothValue(),
  4027                            NS_LITERAL_STRING(ERR_AVRCP_IS_DISCONNECTED));
  4028     return;
  4031   if (playStatus != a2dp->GetPlayStatus()) {
  4032     UpdateNotification(ControlEventId::EVENT_PLAYBACK_STATUS_CHANGED,
  4033                        playStatus);
  4034   } else if (aPosition != a2dp->GetPosition()) {
  4035     UpdateNotification(ControlEventId::EVENT_PLAYBACK_POS_CHANGED, aPosition);
  4038   nsAutoString deviceAddress;
  4039   a2dp->GetAddress(deviceAddress);
  4041   Task* task = new SendPlayStatusTask(deviceAddress,
  4042                                       aDuration,
  4043                                       aPosition,
  4044                                       playStatus,
  4045                                       aRunnable);
  4046   DispatchToDBusThread(task);
  4048   a2dp->UpdatePlayStatus(aDuration, aPosition, playStatus);
  4051 static void
  4052 ControlCallback(DBusMessage* aMsg, void* aParam)
  4054   NS_ENSURE_TRUE_VOID(aMsg);
  4056   BluetoothValue v;
  4057   nsAutoString replyError;
  4058   UnpackVoidMessage(aMsg, nullptr, v, replyError);
  4059   if (!v.get_bool()) {
  4060     BT_WARNING(NS_ConvertUTF16toUTF8(replyError).get());
  4064 class UpdatePlayStatusTask : public Task
  4066 public:
  4067   UpdatePlayStatusTask(const nsAString& aDeviceAddress,
  4068                        int32_t aDuration,
  4069                        int32_t aPosition,
  4070                        ControlPlayStatus aPlayStatus)
  4071     : mDeviceAddress(aDeviceAddress)
  4072     , mDuration(aDuration)
  4073     , mPosition(aPosition)
  4074     , mPlayStatus(aPlayStatus)
  4076     MOZ_ASSERT(!mDeviceAddress.IsEmpty());
  4079   void Run() MOZ_OVERRIDE
  4081     MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
  4082     MOZ_ASSERT(sDBusConnection);
  4083     MOZ_ASSERT(!sAdapterPath.IsEmpty());
  4085     const nsCString objectPath = NS_ConvertUTF16toUTF8(
  4086       GetObjectPathFromAddress(sAdapterPath, mDeviceAddress));
  4088     uint32_t tempPlayStatus = mPlayStatus;
  4090     bool success = sDBusConnection->SendWithReply(
  4091       ControlCallback, nullptr, -1,
  4092       BLUEZ_DBUS_BASE_IFC,
  4093       objectPath.get(),
  4094       DBUS_CTL_IFACE, "UpdatePlayStatus",
  4095       DBUS_TYPE_UINT32, &mDuration,
  4096       DBUS_TYPE_UINT32, &mPosition,
  4097       DBUS_TYPE_UINT32, &tempPlayStatus,
  4098       DBUS_TYPE_INVALID);
  4099     NS_ENSURE_TRUE_VOID(success);
  4102 private:
  4103   const nsString mDeviceAddress;
  4104   int32_t mDuration;
  4105   int32_t mPosition;
  4106   ControlPlayStatus mPlayStatus;
  4107 };
  4109 void
  4110 BluetoothDBusService::UpdatePlayStatus(uint32_t aDuration,
  4111                                        uint32_t aPosition,
  4112                                        ControlPlayStatus aPlayStatus)
  4114   MOZ_ASSERT(NS_IsMainThread());
  4115   NS_ENSURE_TRUE_VOID(this->IsReady());
  4117   BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get();
  4118   NS_ENSURE_TRUE_VOID(a2dp);
  4119   MOZ_ASSERT(a2dp->IsConnected());
  4120   MOZ_ASSERT(a2dp->IsAvrcpConnected());
  4121   MOZ_ASSERT(!sAdapterPath.IsEmpty());
  4123   nsAutoString deviceAddress;
  4124   a2dp->GetAddress(deviceAddress);
  4126   Task* task = new UpdatePlayStatusTask(deviceAddress,
  4127                                         aDuration,
  4128                                         aPosition,
  4129                                         aPlayStatus);
  4130   DispatchToDBusThread(task);
  4133 class UpdateNotificationTask : public Task
  4135 public:
  4136   UpdateNotificationTask(const nsAString& aDeviceAddress,
  4137                          BluetoothDBusService::ControlEventId aEventId,
  4138                          uint64_t aData)
  4139     : mDeviceAddress(aDeviceAddress)
  4140     , mEventId(aEventId)
  4141     , mData(aData)
  4143     MOZ_ASSERT(!mDeviceAddress.IsEmpty());
  4146   void Run() MOZ_OVERRIDE
  4148     MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
  4149     MOZ_ASSERT(sDBusConnection);
  4150     MOZ_ASSERT(!sAdapterPath.IsEmpty());
  4152     const nsCString objectPath = NS_ConvertUTF16toUTF8(
  4153       GetObjectPathFromAddress(sAdapterPath, mDeviceAddress));
  4155     uint16_t eventId = mEventId;
  4157     bool success = sDBusConnection->SendWithReply(
  4158       ControlCallback, nullptr, -1,
  4159       BLUEZ_DBUS_BASE_IFC,
  4160       objectPath.get(),
  4161       DBUS_CTL_IFACE, "UpdateNotification",
  4162       DBUS_TYPE_UINT16, &eventId,
  4163       DBUS_TYPE_UINT64, &mData,
  4164       DBUS_TYPE_INVALID);
  4165     NS_ENSURE_TRUE_VOID(success);
  4168 private:
  4169   const nsString mDeviceAddress;
  4170   int16_t mEventId;
  4171   int32_t mData;
  4172 };
  4174 void
  4175 BluetoothDBusService::UpdateNotification(ControlEventId aEventId,
  4176                                          uint64_t aData)
  4178   MOZ_ASSERT(NS_IsMainThread());
  4179   NS_ENSURE_TRUE_VOID(this->IsReady());
  4181   BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get();
  4182   NS_ENSURE_TRUE_VOID(a2dp);
  4183   MOZ_ASSERT(a2dp->IsConnected());
  4184   MOZ_ASSERT(a2dp->IsAvrcpConnected());
  4185   MOZ_ASSERT(!sAdapterPath.IsEmpty());
  4187   nsAutoString deviceAddress;
  4188   a2dp->GetAddress(deviceAddress);
  4190   Task* task = new UpdateNotificationTask(deviceAddress, aEventId, aData);
  4191   DispatchToDBusThread(task);

mercurial