dom/bluetooth/bluez/BluetoothDBusService.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial