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 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "base/basictypes.h"
9 #include "BluetoothService.h"
11 #include "BluetoothCommon.h"
12 #include "BluetoothA2dpManager.h"
13 #include "BluetoothHfpManager.h"
14 #include "BluetoothHidManager.h"
15 #include "BluetoothManager.h"
16 #include "BluetoothOppManager.h"
17 #include "BluetoothParent.h"
18 #include "BluetoothReplyRunnable.h"
19 #include "BluetoothServiceChildProcess.h"
20 #include "BluetoothUtils.h"
23 #include "mozilla/ClearOnShutdown.h"
24 #include "mozilla/Services.h"
25 #include "mozilla/StaticPtr.h"
26 #include "mozilla/unused.h"
27 #include "mozilla/dom/ContentParent.h"
28 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
29 #include "mozilla/dom/ipc/BlobChild.h"
30 #include "mozilla/dom/ipc/BlobParent.h"
31 #include "nsContentUtils.h"
32 #include "nsIObserverService.h"
33 #include "nsISettingsService.h"
34 #include "nsISystemMessagesInternal.h"
36 #include "nsServiceManagerUtils.h"
38 #include "mozilla/dom/SettingChangeNotificationBinding.h"
40 #if defined(MOZ_WIDGET_GONK)
41 #include "cutils/properties.h"
44 #if defined(MOZ_B2G_BT)
45 #if defined(MOZ_B2G_BT_BLUEZ)
48 * MOZ_B2G_BT and MOZ_B2G_BT_BLUEZ are both defined.
50 #include "BluetoothDBusService.h"
51 #elif defined(MOZ_B2G_BT_BLUEDROID)
54 * MOZ_B2G_BT and MOZ_B2G_BT_BLUEDROID are both defined;
55 * MOZ_B2G_BLUEZ or MOZ_B2G_DAEMON are not defined.
57 #include "BluetoothServiceBluedroid.h"
58 #elif defined(MOZ_B2G_BT_DAEMON)
60 * B2G Bluetooth daemon:
61 * MOZ_B2G_BT, MOZ_B2G_BLUEDROID and MOZ_B2G_BT_DAEMON are defined;
62 * MOZ_B2G_BLUEZ is not defined.
64 #include "BluetoothServiceBluedroid.h"
66 #elif defined(MOZ_BLUETOOTH_DBUS)
69 * MOZ_B2G_BT is not defined; MOZ_BLUETOOTH_DBUS is defined.
71 #include "BluetoothDBusService.h"
76 #define MOZSETTINGS_CHANGED_ID "mozsettings-changed"
77 #define BLUETOOTH_ENABLED_SETTING "bluetooth.enabled"
78 #define BLUETOOTH_DEBUGGING_SETTING "bluetooth.debugging.enabled"
80 #define PROP_BLUETOOTH_ENABLED "bluetooth.isEnabled"
82 #define DEFAULT_SHUTDOWN_TIMER_MS 5000
84 bool gBluetoothDebugFlag
= false;
86 using namespace mozilla
;
87 using namespace mozilla::dom
;
88 USING_BLUETOOTH_NAMESPACE
92 StaticRefPtr
<BluetoothService
> sBluetoothService
;
94 bool sInShutdown
= false;
95 bool sToggleInProgress
= false;
98 ShutdownTimeExceeded(nsITimer
* aTimer
, void* aClosure
)
100 MOZ_ASSERT(NS_IsMainThread());
101 *static_cast<bool*>(aClosure
) = true;
105 GetAllBluetoothActors(InfallibleTArray
<BluetoothParent
*>& aActors
)
107 MOZ_ASSERT(NS_IsMainThread());
108 MOZ_ASSERT(aActors
.IsEmpty());
110 nsAutoTArray
<ContentParent
*, 20> contentActors
;
111 ContentParent::GetAll(contentActors
);
113 for (uint32_t contentIndex
= 0;
114 contentIndex
< contentActors
.Length();
116 MOZ_ASSERT(contentActors
[contentIndex
]);
118 AutoInfallibleTArray
<PBluetoothParent
*, 5> bluetoothActors
;
119 contentActors
[contentIndex
]->ManagedPBluetoothParent(bluetoothActors
);
121 for (uint32_t bluetoothIndex
= 0;
122 bluetoothIndex
< bluetoothActors
.Length();
124 MOZ_ASSERT(bluetoothActors
[bluetoothIndex
]);
126 BluetoothParent
* actor
=
127 static_cast<BluetoothParent
*>(bluetoothActors
[bluetoothIndex
]);
128 aActors
.AppendElement(actor
);
133 } // anonymous namespace
135 BluetoothService::ToggleBtAck::ToggleBtAck(bool aEnabled
)
140 BluetoothService::ToggleBtAck::Run()
142 BluetoothService::AcknowledgeToggleBt(mEnabled
);
147 class BluetoothService::StartupTask MOZ_FINAL
: public nsISettingsServiceCallback
152 NS_IMETHOD
Handle(const nsAString
& aName
, JS::Handle
<JS::Value
> aResult
)
154 MOZ_ASSERT(NS_IsMainThread());
156 if (!aResult
.isBoolean()) {
157 BT_WARNING("Setting for '" BLUETOOTH_ENABLED_SETTING
"' is not a boolean!");
161 // It is theoretically possible to shut down before the first settings check
162 // has completed (though extremely unlikely).
163 if (sBluetoothService
) {
164 return sBluetoothService
->HandleStartupSettingsCheck(aResult
.toBoolean());
170 NS_IMETHOD
HandleError(const nsAString
& aName
)
172 BT_WARNING("Unable to get value for '" BLUETOOTH_ENABLED_SETTING
"'");
177 NS_IMPL_ISUPPORTS(BluetoothService::StartupTask
, nsISettingsServiceCallback
);
179 NS_IMPL_ISUPPORTS(BluetoothService
, nsIObserver
)
182 BluetoothService::IsToggling() const
184 return sToggleInProgress
;
187 BluetoothService::~BluetoothService()
194 BluetoothService::Create()
196 #if defined(MOZ_B2G_BT)
197 if (!IsMainProcess()) {
198 return BluetoothServiceChildProcess::Create();
201 #if defined(MOZ_B2G_BT_BLUEZ)
202 return new BluetoothDBusService();
203 #elif defined(MOZ_B2G_BT_BLUEDROID)
204 return new BluetoothServiceBluedroid();
205 #elif defined(MOZ_B2G_BT_DAEMON)
206 return new BluetoothServiceBluedroid();
208 #elif defined(MOZ_BLUETOOTH_DBUS)
209 return new BluetoothDBusService();
212 BT_WARNING("No platform support for bluetooth!");
217 BluetoothService::Init()
219 MOZ_ASSERT(NS_IsMainThread());
221 nsCOMPtr
<nsIObserverService
> obs
= services::GetObserverService();
222 NS_ENSURE_TRUE(obs
, false);
224 if (NS_FAILED(obs
->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID
,
226 BT_WARNING("Failed to add shutdown observer!");
230 // Only the main process should observe bluetooth settings changes.
231 if (IsMainProcess() &&
232 NS_FAILED(obs
->AddObserver(this, MOZSETTINGS_CHANGED_ID
, false))) {
233 BT_WARNING("Failed to add settings change observer!");
241 BluetoothService::Cleanup()
243 MOZ_ASSERT(NS_IsMainThread());
245 nsCOMPtr
<nsIObserverService
> obs
= services::GetObserverService();
247 (NS_FAILED(obs
->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID
)) ||
248 NS_FAILED(obs
->RemoveObserver(this, MOZSETTINGS_CHANGED_ID
)))) {
249 BT_WARNING("Can't unregister observers!");
254 BluetoothService::RegisterBluetoothSignalHandler(
255 const nsAString
& aNodeName
,
256 BluetoothSignalObserver
* aHandler
)
258 MOZ_ASSERT(NS_IsMainThread());
259 MOZ_ASSERT(aHandler
);
261 BT_LOGD("[S] %s: %s", __FUNCTION__
, NS_ConvertUTF16toUTF8(aNodeName
).get());
263 BluetoothSignalObserverList
* ol
;
264 if (!mBluetoothSignalObserverTable
.Get(aNodeName
, &ol
)) {
265 ol
= new BluetoothSignalObserverList();
266 mBluetoothSignalObserverTable
.Put(aNodeName
, ol
);
269 ol
->AddObserver(aHandler
);
271 // Distribute pending pairing requests when pairing listener has been added
272 // to signal observer table.
273 if (IsMainProcess() &&
274 !mPendingPairReqSignals
.IsEmpty() &&
275 aNodeName
.EqualsLiteral(KEY_PAIRING_LISTENER
)) {
276 for (uint32_t i
= 0; i
< mPendingPairReqSignals
.Length(); ++i
) {
277 DistributeSignal(mPendingPairReqSignals
[i
]);
279 mPendingPairReqSignals
.Clear();
284 BluetoothService::UnregisterBluetoothSignalHandler(
285 const nsAString
& aNodeName
,
286 BluetoothSignalObserver
* aHandler
)
288 MOZ_ASSERT(NS_IsMainThread());
289 MOZ_ASSERT(aHandler
);
291 BT_LOGD("[S] %s: %s", __FUNCTION__
, NS_ConvertUTF16toUTF8(aNodeName
).get());
293 BluetoothSignalObserverList
* ol
;
294 if (mBluetoothSignalObserverTable
.Get(aNodeName
, &ol
)) {
295 ol
->RemoveObserver(aHandler
);
296 // We shouldn't have duplicate instances in the ObserverList, but there's
297 // no appropriate way to do duplication check while registering, so
298 // assertions are added here.
299 MOZ_ASSERT(!ol
->RemoveObserver(aHandler
));
300 if (ol
->Length() == 0) {
301 mBluetoothSignalObserverTable
.Remove(aNodeName
);
305 BT_WARNING("Node was never registered!");
310 RemoveAllSignalHandlers(const nsAString
& aKey
,
311 nsAutoPtr
<BluetoothSignalObserverList
>& aData
,
314 BluetoothSignalObserver
* handler
= static_cast<BluetoothSignalObserver
*>(aUserArg
);
315 aData
->RemoveObserver(handler
);
316 // We shouldn't have duplicate instances in the ObserverList, but there's
317 // no appropriate way to do duplication check while registering, so
318 // assertions are added here.
319 MOZ_ASSERT(!aData
->RemoveObserver(handler
));
320 return aData
->Length() ? PL_DHASH_NEXT
: PL_DHASH_REMOVE
;
324 BluetoothService::UnregisterAllSignalHandlers(BluetoothSignalObserver
* aHandler
)
326 MOZ_ASSERT(NS_IsMainThread());
327 MOZ_ASSERT(aHandler
);
329 mBluetoothSignalObserverTable
.Enumerate(RemoveAllSignalHandlers
, aHandler
);
333 BluetoothService::DistributeSignal(const BluetoothSignal
& aSignal
)
335 MOZ_ASSERT(NS_IsMainThread());
337 BluetoothSignalObserverList
* ol
;
338 if (!mBluetoothSignalObserverTable
.Get(aSignal
.path(), &ol
)) {
339 // If there is no BluetoohPairingListener in observer table, put the signal
340 // into a pending queue of pairing requests and send a system message to
341 // launch bluetooth certified app.
342 if (aSignal
.path().EqualsLiteral(KEY_PAIRING_LISTENER
)) {
343 mPendingPairReqSignals
.AppendElement(aSignal
);
345 BT_ENSURE_TRUE_VOID_BROADCAST_SYSMSG(
346 NS_LITERAL_STRING(SYS_MSG_BT_PAIRING_REQ
),
347 BluetoothValue(EmptyString()));
349 BT_WARNING("No observer registered for path %s",
350 NS_ConvertUTF16toUTF8(aSignal
.path()).get());
355 MOZ_ASSERT(ol
->Length());
356 ol
->Broadcast(aSignal
);
360 BluetoothService::StartBluetooth(bool aIsStartup
,
361 BluetoothReplyRunnable
* aRunnable
)
363 MOZ_ASSERT(NS_IsMainThread());
366 // Don't try to start if we're already shutting down.
367 MOZ_ASSERT(false, "Start called while in shutdown!");
368 return NS_ERROR_FAILURE
;
371 /* When IsEnabled() is true, we don't switch on Bluetooth but we still
372 * send ToggleBtAck task. One special case happens at startup stage. At
373 * startup, the initialization of BluetoothService still has to be done
374 * even if Bluetooth is already enabled.
376 * Please see bug 892392 for more information.
378 if (aIsStartup
|| !IsEnabled()) {
379 // Switch Bluetooth on
380 nsresult rv
= StartInternal(aRunnable
);
382 BT_WARNING("Bluetooth service failed to start!");
386 BT_WARNING("Bluetooth has already been enabled before.");
387 nsRefPtr
<nsRunnable
> runnable
= new BluetoothService::ToggleBtAck(true);
388 if (NS_FAILED(NS_DispatchToMainThread(runnable
))) {
389 BT_WARNING("Failed to dispatch to main thread!");
397 BluetoothService::StopBluetooth(bool aIsStartup
,
398 BluetoothReplyRunnable
* aRunnable
)
400 MOZ_ASSERT(NS_IsMainThread());
402 /* When IsEnabled() is false, we don't switch off Bluetooth but we still
403 * send ToggleBtAck task. One special case happens at startup stage. At
404 * startup, the initialization of BluetoothService still has to be done
405 * even if Bluetooth is disabled.
407 * Please see bug 892392 for more information.
409 if (aIsStartup
|| IsEnabled()) {
410 // Any connected Bluetooth profile would be disconnected.
411 nsresult rv
= StopInternal(aRunnable
);
413 BT_WARNING("Bluetooth service failed to stop!");
417 BT_WARNING("Bluetooth has already been enabled/disabled before.");
418 nsRefPtr
<nsRunnable
> runnable
= new BluetoothService::ToggleBtAck(false);
419 if (NS_FAILED(NS_DispatchToMainThread(runnable
))) {
420 BT_WARNING("Failed to dispatch to main thread!");
428 BluetoothService::StartStopBluetooth(bool aStart
,
430 BluetoothReplyRunnable
* aRunnable
)
434 rv
= StartBluetooth(aIsStartup
, aRunnable
);
436 rv
= StopBluetooth(aIsStartup
, aRunnable
);
442 BluetoothService::SetEnabled(bool aEnabled
)
444 MOZ_ASSERT(NS_IsMainThread());
446 AutoInfallibleTArray
<BluetoothParent
*, 10> childActors
;
447 GetAllBluetoothActors(childActors
);
449 for (uint32_t index
= 0; index
< childActors
.Length(); index
++) {
450 unused
<< childActors
[index
]->SendEnabled(aEnabled
);
454 * mEnabled: real status of bluetooth
455 * aEnabled: expected status of bluetooth
457 if (mEnabled
== aEnabled
) {
458 BT_WARNING("Bluetooth is already %s, or the toggling failed.",
459 mEnabled
? "enabled" : "disabled");
466 BluetoothService::HandleStartup()
468 MOZ_ASSERT(NS_IsMainThread());
469 MOZ_ASSERT(!sToggleInProgress
);
471 nsCOMPtr
<nsISettingsService
> settings
=
472 do_GetService("@mozilla.org/settingsService;1");
473 NS_ENSURE_TRUE(settings
, NS_ERROR_UNEXPECTED
);
475 nsCOMPtr
<nsISettingsServiceLock
> settingsLock
;
476 nsresult rv
= settings
->CreateLock(nullptr, getter_AddRefs(settingsLock
));
477 NS_ENSURE_SUCCESS(rv
, rv
);
479 nsRefPtr
<StartupTask
> callback
= new StartupTask();
480 rv
= settingsLock
->Get(BLUETOOTH_ENABLED_SETTING
, callback
);
481 NS_ENSURE_SUCCESS(rv
, rv
);
483 sToggleInProgress
= true;
488 BluetoothService::HandleStartupSettingsCheck(bool aEnable
)
490 MOZ_ASSERT(NS_IsMainThread());
491 return StartStopBluetooth(aEnable
, true, nullptr);
495 BluetoothService::HandleSettingsChanged(nsISupports
* aSubject
)
497 MOZ_ASSERT(NS_IsMainThread());
499 // The string that we're interested in will be a JSON string that looks like:
500 // {"key":"bluetooth.enabled","value":true}
502 RootedDictionary
<SettingChangeNotification
> setting(nsContentUtils::RootingCx());
503 if (!WrappedJSToDictionary(aSubject
, setting
)) {
506 if (!setting
.mKey
.EqualsASCII(BLUETOOTH_DEBUGGING_SETTING
)) {
509 if (!setting
.mValue
.isBoolean()) {
510 MOZ_ASSERT(false, "Expecting a boolean for 'bluetooth.debugging.enabled'!");
511 return NS_ERROR_UNEXPECTED
;
514 SWITCH_BT_DEBUG(setting
.mValue
.toBoolean());
520 BluetoothService::HandleShutdown()
522 MOZ_ASSERT(NS_IsMainThread());
524 // This is a two phase shutdown. First we notify all child processes that
525 // bluetooth is going away, and then we wait for them to acknowledge. Then we
526 // close down all the bluetooth machinery.
532 AutoInfallibleTArray
<BluetoothParent
*, 10> childActors
;
533 GetAllBluetoothActors(childActors
);
535 if (!childActors
.IsEmpty()) {
536 // Notify child processes that they should stop using bluetooth now.
537 for (uint32_t index
= 0; index
< childActors
.Length(); index
++) {
538 childActors
[index
]->BeginShutdown();
541 // Create a timer to ensure that we don't wait forever for a child process
542 // or the bluetooth threads to finish. If we don't get a timer or can't use
543 // it for some reason then we skip all the waiting entirely since we really
544 // can't afford to hang on shutdown.
545 nsCOMPtr
<nsITimer
> timer
= do_CreateInstance(NS_TIMER_CONTRACTID
);
549 bool timeExceeded
= false;
551 if (NS_SUCCEEDED(timer
->InitWithFuncCallback(ShutdownTimeExceeded
,
553 DEFAULT_SHUTDOWN_TIMER_MS
,
554 nsITimer::TYPE_ONE_SHOT
))) {
555 nsIThread
* currentThread
= NS_GetCurrentThread();
556 MOZ_ASSERT(currentThread
);
558 // Wait for those child processes to acknowledge.
559 while (!timeExceeded
&& !childActors
.IsEmpty()) {
560 if (!NS_ProcessNextEvent(currentThread
)) {
561 MOZ_ASSERT(false, "Something horribly wrong here!");
564 GetAllBluetoothActors(childActors
);
567 if (NS_FAILED(timer
->Cancel())) {
568 MOZ_CRASH("Failed to cancel shutdown timer, this will crash!");
572 MOZ_ASSERT(false, "Failed to initialize shutdown timer!");
577 if (IsEnabled() && NS_FAILED(StopBluetooth(false, nullptr))) {
578 MOZ_ASSERT(false, "Failed to deliver stop message!");
586 BluetoothService::Get()
588 MOZ_ASSERT(NS_IsMainThread());
590 // If we already exist, exit early
591 if (sBluetoothService
) {
592 return sBluetoothService
;
595 // If we're in shutdown, don't create a new instance
597 BT_WARNING("BluetoothService can't be created during shutdown");
601 // Create new instance, register, return
602 sBluetoothService
= BluetoothService::Create();
603 NS_ENSURE_TRUE(sBluetoothService
, nullptr);
605 if (!sBluetoothService
->Init()) {
606 sBluetoothService
->Cleanup();
610 ClearOnShutdown(&sBluetoothService
);
611 return sBluetoothService
;
615 BluetoothService::Observe(nsISupports
* aSubject
, const char* aTopic
,
616 const char16_t
* aData
)
618 MOZ_ASSERT(NS_IsMainThread());
620 if (!strcmp(aTopic
, "profile-after-change")) {
621 return HandleStartup();
624 if (!strcmp(aTopic
, MOZSETTINGS_CHANGED_ID
)) {
625 return HandleSettingsChanged(aSubject
);
628 if (!strcmp(aTopic
, NS_XPCOM_SHUTDOWN_OBSERVER_ID
)) {
629 return HandleShutdown();
632 MOZ_ASSERT(false, "BluetoothService got unexpected topic!");
633 return NS_ERROR_UNEXPECTED
;
637 * Enable/Disable the local adapter.
639 * There is only one adapter on the mobile in current use cases.
640 * In addition, bluedroid couldn't enable/disable a single adapter.
641 * So currently we will turn on/off BT to enable/disable the adapter.
643 * TODO: To support enable/disable single adapter in the future,
644 * we will need to implement EnableDisableInternal for different stacks.
647 BluetoothService::EnableDisable(bool aEnable
,
648 BluetoothReplyRunnable
* aRunnable
)
650 sToggleInProgress
= true;
651 return StartStopBluetooth(aEnable
, false, aRunnable
);
655 BluetoothService::FireAdapterStateChanged(bool aEnable
)
657 MOZ_ASSERT(NS_IsMainThread());
659 InfallibleTArray
<BluetoothNamedValue
> props
;
660 BT_APPEND_NAMED_VALUE(props
, "State", aEnable
);
661 BluetoothValue
value(props
);
663 BluetoothSignal
signal(NS_LITERAL_STRING("PropertyChanged"),
664 NS_LITERAL_STRING(KEY_ADAPTER
), value
);
665 DistributeSignal(signal
);
669 BluetoothService::AcknowledgeToggleBt(bool aEnabled
)
671 MOZ_ASSERT(NS_IsMainThread());
673 #if defined(MOZ_WIDGET_GONK)
674 // This is requested in Bug 836516. With settings this property, WLAN
675 // firmware could be aware of Bluetooth has been turned on/off, so that the
676 // mecahnism of handling coexistence of WIFI and Bluetooth could be started.
678 // In the future, we may have our own way instead of setting a system
679 // property to let firmware developers be able to sense that Bluetooth has
681 if (property_set(PROP_BLUETOOTH_ENABLED
, aEnabled
? "true" : "false") != 0) {
682 BT_WARNING("Failed to set bluetooth enabled property");
687 sBluetoothService
= nullptr;
691 NS_ENSURE_TRUE_VOID(sBluetoothService
);
693 sBluetoothService
->CompleteToggleBt(aEnabled
);
697 BluetoothService::CompleteToggleBt(bool aEnabled
)
699 MOZ_ASSERT(NS_IsMainThread());
701 // Update |mEnabled| of |BluetoothService| object since
702 // |StartInternal| and |StopInternal| have been already
704 SetEnabled(aEnabled
);
705 sToggleInProgress
= false;
707 FireAdapterStateChanged(aEnabled
);