Bumping manifests a=b2g-bump
[gecko.git] / dom / bluetooth2 / BluetoothService.cpp
blob0fffbf610c2bcc604d520d23bb60dac4b821d3fc
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"
22 #include "jsapi.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"
35 #include "nsITimer.h"
36 #include "nsServiceManagerUtils.h"
37 #include "nsXPCOM.h"
38 #include "mozilla/dom/SettingChangeNotificationBinding.h"
40 #if defined(MOZ_WIDGET_GONK)
41 #include "cutils/properties.h"
42 #endif
44 #if defined(MOZ_B2G_BT)
45 #if defined(MOZ_B2G_BT_BLUEZ)
46 /**
47 * B2G blueZ:
48 * MOZ_B2G_BT and MOZ_B2G_BT_BLUEZ are both defined.
50 #include "BluetoothDBusService.h"
51 #elif defined(MOZ_B2G_BT_BLUEDROID)
52 /**
53 * B2G 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)
59 /**
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"
65 #endif
66 #elif defined(MOZ_BLUETOOTH_DBUS)
67 /**
68 * Desktop bluetooth:
69 * MOZ_B2G_BT is not defined; MOZ_BLUETOOTH_DBUS is defined.
71 #include "BluetoothDBusService.h"
72 #else
73 #error No backend
74 #endif
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
90 namespace {
92 StaticRefPtr<BluetoothService> sBluetoothService;
94 bool sInShutdown = false;
95 bool sToggleInProgress = false;
97 void
98 ShutdownTimeExceeded(nsITimer* aTimer, void* aClosure)
100 MOZ_ASSERT(NS_IsMainThread());
101 *static_cast<bool*>(aClosure) = true;
104 void
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();
115 contentIndex++) {
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();
123 bluetoothIndex++) {
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)
136 : mEnabled(aEnabled)
139 NS_METHOD
140 BluetoothService::ToggleBtAck::Run()
142 BluetoothService::AcknowledgeToggleBt(mEnabled);
144 return NS_OK;
147 class BluetoothService::StartupTask MOZ_FINAL : public nsISettingsServiceCallback
149 public:
150 NS_DECL_ISUPPORTS
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!");
158 return NS_OK;
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());
167 return NS_OK;
170 NS_IMETHOD HandleError(const nsAString& aName)
172 BT_WARNING("Unable to get value for '" BLUETOOTH_ENABLED_SETTING "'");
173 return NS_OK;
177 NS_IMPL_ISUPPORTS(BluetoothService::StartupTask, nsISettingsServiceCallback);
179 NS_IMPL_ISUPPORTS(BluetoothService, nsIObserver)
181 bool
182 BluetoothService::IsToggling() const
184 return sToggleInProgress;
187 BluetoothService::~BluetoothService()
189 Cleanup();
192 // static
193 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();
207 #endif
208 #elif defined(MOZ_BLUETOOTH_DBUS)
209 return new BluetoothDBusService();
210 #endif
212 BT_WARNING("No platform support for bluetooth!");
213 return nullptr;
216 bool
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,
225 false))) {
226 BT_WARNING("Failed to add shutdown observer!");
227 return false;
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!");
234 return false;
237 return true;
240 void
241 BluetoothService::Cleanup()
243 MOZ_ASSERT(NS_IsMainThread());
245 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
246 if (obs &&
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!");
253 void
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();
283 void
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);
304 else {
305 BT_WARNING("Node was never registered!");
309 PLDHashOperator
310 RemoveAllSignalHandlers(const nsAString& aKey,
311 nsAutoPtr<BluetoothSignalObserverList>& aData,
312 void* aUserArg)
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;
323 void
324 BluetoothService::UnregisterAllSignalHandlers(BluetoothSignalObserver* aHandler)
326 MOZ_ASSERT(NS_IsMainThread());
327 MOZ_ASSERT(aHandler);
329 mBluetoothSignalObserverTable.Enumerate(RemoveAllSignalHandlers, aHandler);
332 void
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()));
348 } else {
349 BT_WARNING("No observer registered for path %s",
350 NS_ConvertUTF16toUTF8(aSignal.path()).get());
352 return;
355 MOZ_ASSERT(ol->Length());
356 ol->Broadcast(aSignal);
359 nsresult
360 BluetoothService::StartBluetooth(bool aIsStartup,
361 BluetoothReplyRunnable* aRunnable)
363 MOZ_ASSERT(NS_IsMainThread());
365 if (sInShutdown) {
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);
381 if (NS_FAILED(rv)) {
382 BT_WARNING("Bluetooth service failed to start!");
383 return rv;
385 } else {
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!");
393 return NS_OK;
396 nsresult
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);
412 if (NS_FAILED(rv)) {
413 BT_WARNING("Bluetooth service failed to stop!");
414 return rv;
416 } else {
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!");
424 return NS_OK;
427 nsresult
428 BluetoothService::StartStopBluetooth(bool aStart,
429 bool aIsStartup,
430 BluetoothReplyRunnable* aRunnable)
432 nsresult rv;
433 if (aStart) {
434 rv = StartBluetooth(aIsStartup, aRunnable);
435 } else {
436 rv = StopBluetooth(aIsStartup, aRunnable);
438 return rv;
441 void
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");
462 mEnabled = aEnabled;
465 nsresult
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;
484 return NS_OK;
487 nsresult
488 BluetoothService::HandleStartupSettingsCheck(bool aEnable)
490 MOZ_ASSERT(NS_IsMainThread());
491 return StartStopBluetooth(aEnable, true, nullptr);
494 nsresult
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)) {
504 return NS_OK;
506 if (!setting.mKey.EqualsASCII(BLUETOOTH_DEBUGGING_SETTING)) {
507 return NS_OK;
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());
516 return NS_OK;
519 nsresult
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.
528 sInShutdown = true;
530 Cleanup();
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);
546 MOZ_ASSERT(timer);
548 if (timer) {
549 bool timeExceeded = false;
551 if (NS_SUCCEEDED(timer->InitWithFuncCallback(ShutdownTimeExceeded,
552 &timeExceeded,
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!");
562 break;
564 GetAllBluetoothActors(childActors);
567 if (NS_FAILED(timer->Cancel())) {
568 MOZ_CRASH("Failed to cancel shutdown timer, this will crash!");
571 else {
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!");
581 return NS_OK;
584 // static
585 BluetoothService*
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
596 if (sInShutdown) {
597 BT_WARNING("BluetoothService can't be created during shutdown");
598 return nullptr;
601 // Create new instance, register, return
602 sBluetoothService = BluetoothService::Create();
603 NS_ENSURE_TRUE(sBluetoothService, nullptr);
605 if (!sBluetoothService->Init()) {
606 sBluetoothService->Cleanup();
607 return nullptr;
610 ClearOnShutdown(&sBluetoothService);
611 return sBluetoothService;
614 nsresult
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.
646 nsresult
647 BluetoothService::EnableDisable(bool aEnable,
648 BluetoothReplyRunnable* aRunnable)
650 sToggleInProgress = true;
651 return StartStopBluetooth(aEnable, false, aRunnable);
654 void
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);
668 void
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
680 // been toggled.
681 if (property_set(PROP_BLUETOOTH_ENABLED, aEnabled ? "true" : "false") != 0) {
682 BT_WARNING("Failed to set bluetooth enabled property");
684 #endif
686 if (sInShutdown) {
687 sBluetoothService = nullptr;
688 return;
691 NS_ENSURE_TRUE_VOID(sBluetoothService);
693 sBluetoothService->CompleteToggleBt(aEnabled);
696 void
697 BluetoothService::CompleteToggleBt(bool aEnabled)
699 MOZ_ASSERT(NS_IsMainThread());
701 // Update |mEnabled| of |BluetoothService| object since
702 // |StartInternal| and |StopInternal| have been already
703 // done.
704 SetEnabled(aEnabled);
705 sToggleInProgress = false;
707 FireAdapterStateChanged(aEnabled);