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